authorgravatar for anthonyarian96@gmail.comAnthony Arian <anthonyarian96@gmail.com> 2020-07-20 10:25:54+01:00
committergravatar for anthonyarian96@gmail.comAnthony Arian <anthonyarian96@gmail.com> 2020-07-20 10:25:54+01:00
log3658dd5e89cd16c011bdc52d334c1308f440157b
tree09564ab2db65acc4a52d82bccbf0eb572fbc865f
parent68fe3e116d9c4bde67df990b8e0cbb3e70fc98b2
parent596ca6cf70cf43c27e31bbcfc36bcdc70b13897a

Merge branch 'master' of https://github.com/ziglang/zig into 5002-fix-entrypoint-with-winmain


255 files changed, 16493 insertions(+), 7827 deletions(-)

.github/FUNDING.yml+1-1
...@@ -1 +1 @@...@@ -1 +1 @@
1github: [andrewrk]1github: [ziglang]
CMakeLists.txt+12
...@@ -53,6 +53,8 @@ set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not com...@@ -53,6 +53,8 @@ set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not com
53set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries")53set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries")
54set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation")54set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation")
55set(ZIG_PREFER_CLANG_CPP_DYLIB off CACHE BOOL "Try to link against -lclang-cpp")55set(ZIG_PREFER_CLANG_CPP_DYLIB off CACHE BOOL "Try to link against -lclang-cpp")
56set(ZIG_WORKAROUND_4799 off CACHE BOOL "workaround for https://github.com/ziglang/zig/issues/4799")
57set(ZIG_WORKAROUND_POLLY_SO off CACHE STRING "workaround for https://github.com/ziglang/zig/issues/4799")
56set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache if available")58set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache if available")
5759
58if(CCACHE_PROGRAM AND ZIG_USE_CCACHE)60if(CCACHE_PROGRAM AND ZIG_USE_CCACHE)
...@@ -88,6 +90,11 @@ if(APPLE AND ZIG_STATIC)...@@ -88,6 +90,11 @@ if(APPLE AND ZIG_STATIC)
88 list(APPEND LLVM_LIBRARIES "${ZLIB}")90 list(APPEND LLVM_LIBRARIES "${ZLIB}")
89endif()91endif()
9092
93if(APPLE AND ZIG_WORKAROUND_4799)
94 # eg: ${CMAKE_PREFIX_PATH} could be /usr/local/opt/llvm/
95 list(APPEND LLVM_LIBRARIES "-Wl,${CMAKE_PREFIX_PATH}/lib/libPolly.a" "-Wl,${CMAKE_PREFIX_PATH}/lib/libPollyPPCG.a" "-Wl,${CMAKE_PREFIX_PATH}/lib/libPollyISL.a")
96endif()
97
91set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zig_cpp")98set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zig_cpp")
9299
93# Handle multi-config builds and place each into a common lib. The VS generator100# Handle multi-config builds and place each into a common lib. The VS generator
...@@ -288,6 +295,7 @@ set(ZIG_SOURCES...@@ -288,6 +295,7 @@ set(ZIG_SOURCES
288 "${CMAKE_SOURCE_DIR}/src/target.cpp"295 "${CMAKE_SOURCE_DIR}/src/target.cpp"
289 "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"296 "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
290 "${CMAKE_SOURCE_DIR}/src/util.cpp"297 "${CMAKE_SOURCE_DIR}/src/util.cpp"
298 "${CMAKE_SOURCE_DIR}/src/softfloat_ext.cpp"
291 "${ZIG_SOURCES_MEM_PROFILE}"299 "${ZIG_SOURCES_MEM_PROFILE}"
292)300)
293set(OPTIMIZED_C_SOURCES301set(OPTIMIZED_C_SOURCES
...@@ -396,11 +404,15 @@ add_library(zig_cpp STATIC ${ZIG_CPP_SOURCES})...@@ -396,11 +404,15 @@ add_library(zig_cpp STATIC ${ZIG_CPP_SOURCES})
396set_target_properties(zig_cpp PROPERTIES404set_target_properties(zig_cpp PROPERTIES
397 COMPILE_FLAGS ${EXE_CFLAGS}405 COMPILE_FLAGS ${EXE_CFLAGS}
398)406)
407
399target_link_libraries(zig_cpp LINK_PUBLIC408target_link_libraries(zig_cpp LINK_PUBLIC
400 ${CLANG_LIBRARIES}409 ${CLANG_LIBRARIES}
401 ${LLD_LIBRARIES}410 ${LLD_LIBRARIES}
402 ${LLVM_LIBRARIES}411 ${LLVM_LIBRARIES}
403)412)
413if(ZIG_WORKAROUND_POLLY_SO)
414 target_link_libraries(zig_cpp LINK_PUBLIC "-Wl,${ZIG_WORKAROUND_POLLY_SO}")
415endif()
404416
405add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES})417add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES})
406set_target_properties(opt_c_util PROPERTIES418set_target_properties(opt_c_util PROPERTIES
CONTRIBUTING.md+5
...@@ -152,6 +152,11 @@ The relevant tests for this feature are:...@@ -152,6 +152,11 @@ The relevant tests for this feature are:
152 same, and that the program exits cleanly. This kind of test coverage is preferred, when152 same, and that the program exits cleanly. This kind of test coverage is preferred, when
153 possible, because it makes sure that the resulting Zig code is actually viable.153 possible, because it makes sure that the resulting Zig code is actually viable.
154154
155 * `test/stage1/behavior/translate_c_macros.zig` - each test case consists of a Zig test
156 which checks that the relevant macros in `test/stage1/behavior/translate_c_macros.h`.
157 have the correct values. Macros have to be tested separately since they are expanded by
158 Clang in `run_translated_c` tests.
159
155 * `test/translate_c.zig` - each test case is C code, with a list of expected strings which160 * `test/translate_c.zig` - each test case is C code, with a list of expected strings which
156 must be found in the resulting Zig code. This kind of test is more precise in what it161 must be found in the resulting Zig code. This kind of test is more precise in what it
157 measures, but does not provide test coverage of whether the resulting Zig code is valid.162 measures, but does not provide test coverage of whether the resulting Zig code is valid.
README.md+6-2
...@@ -51,6 +51,8 @@ cmake .....@@ -51,6 +51,8 @@ cmake ..
51make install51make install
52```52```
5353
54Need help? [Troubleshooting Build Issues](https://github.com/ziglang/zig/wiki/Troubleshooting-Build-Issues)
55
54##### MacOS56##### MacOS
5557
56```58```
...@@ -64,9 +66,11 @@ make install...@@ -64,9 +66,11 @@ make install
6466
65You will now run into this issue:67You will now run into this issue:
66[homebrew and llvm 10 packages in apt.llvm.org are broken with undefined reference to getPollyPluginInfo](https://github.com/ziglang/zig/issues/4799)68[homebrew and llvm 10 packages in apt.llvm.org are broken with undefined reference to getPollyPluginInfo](https://github.com/ziglang/zig/issues/4799)
69or
70[error: unable to create target: 'Unable to find target for this triple (no targets are registered)'](https://github.com/ziglang/zig/issues/5055),
71in which case try `-DZIG_WORKAROUND_4799=ON`
6772
68Please help upstream LLVM and Homebrew solve this issue, there is nothing Zig73Hopefully this will be fixed upstream with LLVM 10.0.1.
69can do about it. See that issue for a workaround you can do in the meantime.
7074
71##### Windows75##### Windows
7276
build.zig+43-27
...@@ -34,26 +34,12 @@ pub fn build(b: *Builder) !void {...@@ -34,26 +34,12 @@ pub fn build(b: *Builder) !void {
3434
35 const test_step = b.step("test", "Run all the tests");35 const test_step = b.step("test", "Run all the tests");
3636
37 const config_h_text = if (b.option(
38 []const u8,
39 "config_h",
40 "Path to the generated config.h",
41 )) |config_h_path|
42 try std.fs.cwd().readFileAlloc(b.allocator, toNativePathSep(b, config_h_path), max_config_h_bytes)
43 else
44 try findAndReadConfigH(b);
45
46 var test_stage2 = b.addTest("src-self-hosted/test.zig");37 var test_stage2 = b.addTest("src-self-hosted/test.zig");
47 test_stage2.setBuildMode(.Debug); // note this is only the mode of the test harness38 test_stage2.setBuildMode(.Debug); // note this is only the mode of the test harness
48 test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig");39 test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig");
4940
50 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});41 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
5142
52 var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
53 exe.setBuildMode(mode);
54 test_step.dependOn(&exe.step);
55 b.default_step.dependOn(&exe.step);
56
57 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;43 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
58 const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;44 const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
59 const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;45 const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;
...@@ -63,17 +49,44 @@ pub fn build(b: *Builder) !void {...@@ -63,17 +49,44 @@ pub fn build(b: *Builder) !void {
6349
64 const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;50 const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
65 const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse false;51 const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse false;
66 if (enable_llvm) {52 const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h");
67 var ctx = parseConfigH(b, config_h_text);
68 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
6953
70 try configureStage2(b, exe, ctx);
71 }
72 if (!only_install_lib_files) {54 if (!only_install_lib_files) {
73 exe.install();55 var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
56 exe.setBuildMode(mode);
57 test_step.dependOn(&exe.step);
58 b.default_step.dependOn(&exe.step);
59
60 if (enable_llvm) {
61 const config_h_text = if (config_h_path_option) |config_h_path|
62 try std.fs.cwd().readFileAlloc(b.allocator, toNativePathSep(b, config_h_path), max_config_h_bytes)
63 else
64 try findAndReadConfigH(b);
65
66 var ctx = parseConfigH(b, config_h_text);
67 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
68
69 try configureStage2(b, exe, ctx);
70 }
71 if (!only_install_lib_files) {
72 exe.install();
73 }
74 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
75 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
76 if (link_libc) exe.linkLibC();
77
78 exe.addBuildOption(bool, "enable_tracy", tracy != null);
79 if (tracy) |tracy_path| {
80 const client_cpp = fs.path.join(
81 b.allocator,
82 &[_][]const u8{ tracy_path, "TracyClient.cpp" },
83 ) catch unreachable;
84 exe.addIncludeDir(tracy_path);
85 exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" });
86 exe.linkSystemLibraryName("c++");
87 exe.linkLibC();
88 }
74 }89 }
75 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
76 if (link_libc) exe.linkLibC();
7790
78 b.installDirectory(InstallDirectoryOptions{91 b.installDirectory(InstallDirectoryOptions{
79 .source_dir = "lib",92 .source_dir = "lib",
...@@ -126,7 +139,10 @@ pub fn build(b: *Builder) !void {...@@ -126,7 +139,10 @@ pub fn build(b: *Builder) !void {
126 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));139 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
127 test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes));140 test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes));
128 test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));141 test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
129 test_step.dependOn(tests.addCliTests(b, test_filter, modes));142 const test_cli = tests.addCliTests(b, test_filter, modes);
143 const test_cli_step = b.step("test-cli", "Run zig cli tests");
144 test_cli_step.dependOn(test_cli);
145 test_step.dependOn(test_cli);
130 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));146 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
131 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));147 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
132 test_step.dependOn(tests.addTranslateCTests(b, test_filter));148 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
...@@ -137,7 +153,7 @@ pub fn build(b: *Builder) !void {...@@ -137,7 +153,7 @@ pub fn build(b: *Builder) !void {
137 test_step.dependOn(docs_step);153 test_step.dependOn(docs_step);
138}154}
139155
140fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void {156fn dependOnLib(b: *Builder, lib_exe_obj: anytype, dep: LibraryDep) void {
141 for (dep.libdirs.items) |lib_dir| {157 for (dep.libdirs.items) |lib_dir| {
142 lib_exe_obj.addLibPath(lib_dir);158 lib_exe_obj.addLibPath(lib_dir);
143 }159 }
...@@ -177,7 +193,7 @@ fn fileExists(filename: []const u8) !bool {...@@ -177,7 +193,7 @@ fn fileExists(filename: []const u8) !bool {
177 return true;193 return true;
178}194}
179195
180fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void {196fn addCppLib(b: *Builder, lib_exe_obj: anytype, cmake_binary_dir: []const u8, lib_name: []const u8) void {
181 lib_exe_obj.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{197 lib_exe_obj.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{
182 cmake_binary_dir,198 cmake_binary_dir,
183 "zig_cpp",199 "zig_cpp",
...@@ -259,7 +275,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {...@@ -259,7 +275,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
259 return result;275 return result;
260}276}
261277
262fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {278fn configureStage2(b: *Builder, exe: anytype, ctx: Context) !void {
263 exe.addIncludeDir("src");279 exe.addIncludeDir("src");
264 exe.addIncludeDir(ctx.cmake_binary_dir);280 exe.addIncludeDir(ctx.cmake_binary_dir);
265 addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");281 addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");
...@@ -324,7 +340,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {...@@ -324,7 +340,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
324fn addCxxKnownPath(340fn addCxxKnownPath(
325 b: *Builder,341 b: *Builder,
326 ctx: Context,342 ctx: Context,
327 exe: var,343 exe: anytype,
328 objname: []const u8,344 objname: []const u8,
329 errtxt: ?[]const u8,345 errtxt: ?[]const u8,
330) !void {346) !void {
ci/azure/linux_script+5-1
...@@ -12,7 +12,7 @@ sudo apt-get update -q...@@ -12,7 +12,7 @@ sudo apt-get update -q
1212
13sudo apt-get remove -y llvm-*13sudo apt-get remove -y llvm-*
14sudo rm -rf /usr/local/*14sudo rm -rf /usr/local/*
15sudo apt-get install -y libxml2-dev libclang-10-dev llvm-10 llvm-10-dev liblld-10-dev cmake s3cmd gcc-7 g++-7 ninja-build15sudo apt-get install -y libxml2-dev libclang-10-dev llvm-10 llvm-10-dev liblld-10-dev cmake s3cmd gcc-7 g++-7 ninja-build tidy
1616
17QEMUBASE="qemu-linux-x86_64-5.0.0-49ee115552"17QEMUBASE="qemu-linux-x86_64-5.0.0-49ee115552"
18wget https://ziglang.org/deps/$QEMUBASE.tar.xz18wget https://ziglang.org/deps/$QEMUBASE.tar.xz
...@@ -51,6 +51,10 @@ cd build...@@ -51,6 +51,10 @@ cd build
51cmake .. -DCMAKE_BUILD_TYPE=Release -GNinja51cmake .. -DCMAKE_BUILD_TYPE=Release -GNinja
52ninja install52ninja install
53./zig build test -Denable-qemu -Denable-wasmtime53./zig build test -Denable-qemu -Denable-wasmtime
54
55# look for HTML errors
56tidy -qe ../zig-cache/langref.html
57
54VERSION="$(./zig version)"58VERSION="$(./zig version)"
5559
56if [ "${BUILD_REASON}" != "PullRequest" ]; then60if [ "${BUILD_REASON}" != "PullRequest" ]; then
ci/azure/pipelines.yml+13-5
...@@ -40,12 +40,20 @@ jobs:...@@ -40,12 +40,20 @@ jobs:
40 timeoutInMinutes: 36040 timeoutInMinutes: 360
4141
42 steps:42 steps:
43 - powershell: |
44 (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2020-06-02/msys2-base-x86_64-20200602.sfx.exe", "sfx.exe")
45 .\sfx.exe -y -o\
46 del sfx.exe
47 displayName: Download/Extract/Install MSYS2
43 - script: |48 - script: |
44 git clone https://github.com/msys2/msys2-ci-base.git %CD:~0,2%\msys6449 @REM install updated filesystem package first without dependency checking
45 %CD:~0,2%\msys64\usr\bin\rm -rf %CD:~0,2%\msys64\.git50 @REM because of: https://github.com/msys2/MSYS2-packages/issues/2021
46 set PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem51 %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Sydd filesystem"
47 %CD:~0,2%\msys64\usr\bin\pacman --noconfirm -Syyuu52 displayName: Workaround filesystem dash MSYS2 dependency issue
48 displayName: Install and Update MSYS253 - script: |
54 %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
55 %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
56 displayName: Update MSYS2
49 - task: DownloadSecureFile@157 - task: DownloadSecureFile@1
50 inputs:58 inputs:
51 secureFile: s3cfg59 secureFile: s3cfg
ci/azure/windows_msvc_install+1-1
...@@ -4,7 +4,7 @@ set -x...@@ -4,7 +4,7 @@ set -x
4set -e4set -e
55
6pacman -Su --needed --noconfirm6pacman -Su --needed --noconfirm
7pacman -S --needed --noconfirm wget p7zip python3-pip7pacman -S --needed --noconfirm wget p7zip python3-pip tar xz
8pip install s3cmd8pip install s3cmd
9wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz"9wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz"
10tar xf llvm+clang+lld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz10tar xf llvm+clang+lld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz
doc/docgen.zig+7-6
...@@ -212,7 +212,7 @@ const Tokenizer = struct {...@@ -212,7 +212,7 @@ const Tokenizer = struct {
212 }212 }
213};213};
214214
215fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: var) anyerror {215fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: anytype) anyerror {
216 const loc = tokenizer.getTokenLocation(token);216 const loc = tokenizer.getTokenLocation(token);
217 const args_prefix = .{ tokenizer.source_file_name, loc.line + 1, loc.column + 1 };217 const args_prefix = .{ tokenizer.source_file_name, loc.line + 1, loc.column + 1 };
218 warn("{}:{}:{}: error: " ++ fmt ++ "\n", args_prefix ++ args);218 warn("{}:{}:{}: error: " ++ fmt ++ "\n", args_prefix ++ args);
...@@ -392,7 +392,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {...@@ -392,7 +392,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
392 .n = header_stack_size,392 .n = header_stack_size,
393 },393 },
394 });394 });
395 if (try urls.put(urlized, tag_token)) |entry| {395 if (try urls.fetchPut(urlized, tag_token)) |entry| {
396 parseError(tokenizer, tag_token, "duplicate header url: #{}", .{urlized}) catch {};396 parseError(tokenizer, tag_token, "duplicate header url: #{}", .{urlized}) catch {};
397 parseError(tokenizer, entry.value, "other tag here", .{}) catch {};397 parseError(tokenizer, entry.value, "other tag here", .{}) catch {};
398 return error.ParseError;398 return error.ParseError;
...@@ -634,7 +634,7 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {...@@ -634,7 +634,7 @@ fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {
634 return buf.toOwnedSlice();634 return buf.toOwnedSlice();
635}635}
636636
637fn writeEscaped(out: var, input: []const u8) !void {637fn writeEscaped(out: anytype, input: []const u8) !void {
638 for (input) |c| {638 for (input) |c| {
639 try switch (c) {639 try switch (c) {
640 '&' => out.writeAll("&amp;"),640 '&' => out.writeAll("&amp;"),
...@@ -765,7 +765,7 @@ fn isType(name: []const u8) bool {...@@ -765,7 +765,7 @@ fn isType(name: []const u8) bool {
765 return false;765 return false;
766}766}
767767
768fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Token, raw_src: []const u8) !void {768fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token: Token, raw_src: []const u8) !void {
769 const src = mem.trim(u8, raw_src, " \n");769 const src = mem.trim(u8, raw_src, " \n");
770 try out.writeAll("<code class=\"zig\">");770 try out.writeAll("<code class=\"zig\">");
771 var tokenizer = std.zig.Tokenizer.init(src);771 var tokenizer = std.zig.Tokenizer.init(src);
...@@ -825,6 +825,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok...@@ -825,6 +825,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
825 .Keyword_volatile,825 .Keyword_volatile,
826 .Keyword_allowzero,826 .Keyword_allowzero,
827 .Keyword_while,827 .Keyword_while,
828 .Keyword_anytype,
828 => {829 => {
829 try out.writeAll("<span class=\"tok-kw\">");830 try out.writeAll("<span class=\"tok-kw\">");
830 try writeEscaped(out, src[token.loc.start..token.loc.end]);831 try writeEscaped(out, src[token.loc.start..token.loc.end]);
...@@ -977,12 +978,12 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok...@@ -977,12 +978,12 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
977 try out.writeAll("</code>");978 try out.writeAll("</code>");
978}979}
979980
980fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: var, source_token: Token) !void {981fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: anytype, source_token: Token) !void {
981 const raw_src = docgen_tokenizer.buffer[source_token.start..source_token.end];982 const raw_src = docgen_tokenizer.buffer[source_token.start..source_token.end];
982 return tokenizeAndPrintRaw(docgen_tokenizer, out, source_token, raw_src);983 return tokenizeAndPrintRaw(docgen_tokenizer, out, source_token, raw_src);
983}984}
984985
985fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void {986fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: anytype, zig_exe: []const u8) !void {
986 var code_progress_index: usize = 0;987 var code_progress_index: usize = 0;
987988
988 var env_map = try process.getEnvMap(allocator);989 var env_map = try process.getEnvMap(allocator);
doc/langref.html.in+304-145
...@@ -97,7 +97,7 @@...@@ -97,7 +97,7 @@
97 margin: auto;97 margin: auto;
98 }98 }
9999
100 #index {100 #toc {
101 padding: 0 1em;101 padding: 0 1em;
102 }102 }
103103
...@@ -105,7 +105,7 @@...@@ -105,7 +105,7 @@
105 #main-wrapper {105 #main-wrapper {
106 flex-direction: row;106 flex-direction: row;
107 }107 }
108 #contents-wrapper, #index {108 #contents-wrapper, #toc {
109 overflow: auto;109 overflow: auto;
110 }110 }
111 }111 }
...@@ -181,7 +181,7 @@...@@ -181,7 +181,7 @@
181 </head>181 </head>
182 <body>182 <body>
183 <div id="main-wrapper">183 <div id="main-wrapper">
184 <div id="index">184 <div id="toc">
185 <a href="https://ziglang.org/documentation/0.1.1/">0.1.1</a> |185 <a href="https://ziglang.org/documentation/0.1.1/">0.1.1</a> |
186 <a href="https://ziglang.org/documentation/0.2.0/">0.2.0</a> |186 <a href="https://ziglang.org/documentation/0.2.0/">0.2.0</a> |
187 <a href="https://ziglang.org/documentation/0.3.0/">0.3.0</a> |187 <a href="https://ziglang.org/documentation/0.3.0/">0.3.0</a> |
...@@ -189,7 +189,7 @@...@@ -189,7 +189,7 @@
189 <a href="https://ziglang.org/documentation/0.5.0/">0.5.0</a> |189 <a href="https://ziglang.org/documentation/0.5.0/">0.5.0</a> |
190 <a href="https://ziglang.org/documentation/0.6.0/">0.6.0</a> |190 <a href="https://ziglang.org/documentation/0.6.0/">0.6.0</a> |
191 master191 master
192 <h1>Index</h1>192 <h1>Contents</h1>
193 {#nav#}193 {#nav#}
194 </div>194 </div>
195 <div id="contents-wrapper"><div id="contents">195 <div id="contents-wrapper"><div id="contents">
...@@ -218,6 +218,8 @@...@@ -218,6 +218,8 @@
218 </p>218 </p>
219 <p>219 <p>
220 The code samples in this document are compiled and tested as part of the main test suite of Zig.220 The code samples in this document are compiled and tested as part of the main test suite of Zig.
221 </p>
222 <p>
221 This HTML document depends on no external files, so you can use it offline.223 This HTML document depends on no external files, so you can use it offline.
222 </p>224 </p>
223 <p>225 <p>
...@@ -231,26 +233,113 @@...@@ -231,26 +233,113 @@
231const std = @import("std");233const std = @import("std");
232234
233pub fn main() !void {235pub fn main() !void {
234 const stdout = std.io.getStdOut().outStream();236 const stdout = std.io.getStdOut().writer();
235 try stdout.print("Hello, {}!\n", .{"world"});237 try stdout.print("Hello, {}!\n", .{"world"});
236}238}
237 {#code_end#}239 {#code_end#}
238 <p>240 <p>
239 Usually you don't want to write to stdout. You want to write to stderr. And you241 The Zig code sample above demonstrates one way to create a program that will output <code>Hello, world!</code>.
240 don't care if it fails. It's more like a <em>warning message</em> that you want
241 to emit. For that you can use a simpler API:
242 </p>242 </p>
243 {#code_begin|exe|hello#}243 <p>
244const warn = @import("std").debug.warn;244 The code sample shows the contents of a file named <code>hello.zig</code>. Files storing Zig
245 source code are {#link|UTF-8 encoded|Source Encoding#} text files. The files storing
246 Zig source code are usually named with the <code>.zig</code> extension.
247 </p>
248 <p>
249 Following the <code>hello.zig</code> Zig code sample, the {#link|Zig Build System#} is used
250 to build an executable program from the <code>hello.zig</code> source code. Then, the
251 <code>hello</code> program is executed showing its output <code>Hello, world!</code>. The
252 lines beginning with <code>$</code> represent command line prompts and a command.
253 Everything else is program output.
254 </p>
255 <p>
256 The code sample begins by adding Zig's Standard Library to the build using the {#link|@import#} builtin function.
257 The {#syntax#}@import("std"){#endsyntax#} function call creates a structure to represent the Standard Library.
258 The code then makes a {#link|top-level declaration|Global Variables#} of a
259 {#link|constant identifier|Assignment#}, named <code>std</code>, for easy access to
260 <a href="https://github.com/ziglang/zig/wiki/FAQ#where-is-the-documentation-for-the-zig-standard-library">Zig's standard library</a>.
261 </p>
262 <p>
263 Next, a {#link|public function|Functions#}, {#syntax#}pub fn{#endsyntax#}, named <code>main</code>
264 is declared. The <code>main</code> function is necessary because it tells the Zig compiler where the start of
265 the program exists. Programs designed to be executed will need a {#syntax#}pub fn main{#endsyntax#} function.
266 For more advanced use cases, Zig offers other features to inform the compiler where the start of
267 the program exists. Libraries, on the other hand, do not need a <code>main</code> function because
268 library code is usually called by other programs.
269 </p>
270 <p>
271 A function is a block of any number of statements and expressions that, as a whole, perform a task.
272 Functions may or may not return data after they are done performing their task. If a function
273 cannot perform its task, it might return an error. Zig makes all of this explicit.
274 </p>
275 <p>
276 In the <code>hello.zig</code> code sample, the <code>main</code> function is declared
277 with the {#syntax#}!void{#endsyntax#} return type. This return type is known as an {#link|Error Union Type#}.
278 This syntax tells the Zig compiler that the function will either return an
279 error or a value. An error union type combines an {#link|Error Set Type#} and a {#link|Primitive Type|Primitive Types#}.
280 The full form of an error union type is
281 <code>&lt;error set type&gt;</code>{#syntax#}!{#endsyntax#}<code>&lt;primitive type&gt;</code>. In the code
282 sample, the error set type is not explicitly written on the left side of the {#syntax#}!{#endsyntax#} operator.
283 When written this way, the error set type is a special kind of error union type that has an
284 {#link|inferred error set type|Inferred Error Sets#}. The {#syntax#}void{#endsyntax#} after the {#syntax#}!{#endsyntax#} operator
285 tells the compiler that the function will not return a value under normal circumstances (i.e. no errors occur).
286 </p>
287 <p>
288 Note to experienced programmers: Zig also has the boolean {#link|operator|Operators#} {#syntax#}!a{#endsyntax#}
289 where {#syntax#}a{#endsyntax#} is a value of type {#syntax#}bool{#endsyntax#}. Error union types contain the
290 name of the type in the syntax: {#syntax#}!{#endsyntax#}<code>&lt;primitive type&gt;</code>.
291 </p>
292 <p>
293 In Zig, a function's block of statements and expressions are surrounded by <code>{</code> and
294 <code>}</code> curly-braces. Inside of the <code>main</code> function are expressions that perform
295 the task of outputting <code>Hello, world!</code> to standard output.
296 </p>
297 <p>
298 First, a constant identifier, <code>stdout</code>, is initialized to represent standard output's
299 writer. Then, the program tries to print the <code>Hello, world!</code>
300 message to standard output.
301 </p>
302 <p>
303 Functions sometimes need information to perform their task. In Zig, information is passed
304 to functions between open <code>(</code> and close <code>)</code> parenthesis placed after
305 the function's name. This information is also known as arguments. When there are
306 multiple arguments passed to a function, they are separated by commas <code>,</code>.
307 </p>
308 <p>
309 The two arguments passed to the <code>stdout.print()</code> function, <code>"Hello, {}!\n"</code>
310 and <code>.{"world"}</code>, are evaluated at {#link|compile-time|comptime#}. The code sample is
311 purposely written to show how to perform {#link|string|String Literals and Character Literals#}
312 substitution in the <code>print</code> function. The curly-braces inside of the first argument
313 are substituted with the compile-time known value inside of the second argument
314 (known as an {#link|anonymous struct literal|Anonymous Struct Literals#}). The <code>\n</code>
315 inside of the double-quotes of the first argument is the {#link|escape sequence|Escape Sequences#} for the
316 newline character. The {#link|try#} expression evaluates the result of <code>stdout.print</code>.
317 If the result is an error, then the {#syntax#}try{#endsyntax#} expression will return from
318 <code>main</code> with the error. Otherwise, the program will continue. In this case, there are no
319 more statements or expressions left to execute in the <code>main</code> function, so the program exits.
320 </p>
321 <p>
322 In Zig, the standard output writer's <code>print</code> function is allowed to fail because
323 it is actually a function defined as part of a generic Writer. Consider a generic Writer that
324 represents writing data to a file. When the disk is full, a write to the file will fail.
325 However, we typically do not expect writing text to the standard output to fail. To avoid having
326 to handle the failure case of printing to standard output, you can use alternate functions: the
327 <code>std.log</code> function for proper logging or the <code>std.debug.print</code> function.
328 This documentation will use the latter option to print to standard error (stderr) and silently return
329 on failure. The next code sample, <code>hello_again.zig</code> demonstrates the use of
330 <code>std.debug.print</code>.
331 </p>
332 {#code_begin|exe|hello_again#}
333const print = @import("std").debug.print;
245334
246pub fn main() void {335pub fn main() void {
247 warn("Hello, world!\n", .{});336 print("Hello, world!\n", .{});
248}337}
249 {#code_end#}338 {#code_end#}
250 <p>339 <p>
251 Note that you can leave off the {#syntax#}!{#endsyntax#} from the return type because {#syntax#}warn{#endsyntax#} cannot fail.340 Note that you can leave off the {#syntax#}!{#endsyntax#} from the return type because <code>std.debug.print</code> cannot fail.
252 </p>341 </p>
253 {#see_also|Values|@import|Errors|Root Source File#}342 {#see_also|Values|@import|Errors|Root Source File|Source Encoding#}
254 {#header_close#}343 {#header_close#}
255 {#header_open|Comments#}344 {#header_open|Comments#}
256 {#code_begin|test|comments#}345 {#code_begin|test|comments#}
...@@ -303,11 +392,23 @@ const Timestamp = struct {...@@ -303,11 +392,23 @@ const Timestamp = struct {
303 in the middle of an expression, or just before a non-doc comment.392 in the middle of an expression, or just before a non-doc comment.
304 </p>393 </p>
305 {#header_close#}394 {#header_close#}
395 {#header_open|Top-Level Doc Comments#}
396 <p>User documentation that doesn't belong to whatever
397 immediately follows it, like package-level documentation, goes
398 in top-level doc comments. A top-level doc comment is one that
399 begins with two slashes and an exclamation point:
400 {#syntax#}//!{#endsyntax#}.</p>
401 {#code_begin|syntax|tldoc_comments#}
402//! This module provides functions for retrieving the current date and
403//! time with varying degrees of precision and accuracy. It does not
404//! depend on libc, but will use functions from it if available.
405 {#code_end#}
406 {#header_close#}
306 {#header_close#}407 {#header_close#}
307 {#header_open|Values#}408 {#header_open|Values#}
308 {#code_begin|exe|values#}409 {#code_begin|exe|values#}
309// Top-level declarations are order-independent:410// Top-level declarations are order-independent:
310const warn = std.debug.warn;411const print = std.debug.print;
311const std = @import("std");412const std = @import("std");
312const os = std.os;413const os = std.os;
313const assert = std.debug.assert;414const assert = std.debug.assert;
...@@ -315,14 +416,14 @@ const assert = std.debug.assert;...@@ -315,14 +416,14 @@ const assert = std.debug.assert;
315pub fn main() void {416pub fn main() void {
316 // integers417 // integers
317 const one_plus_one: i32 = 1 + 1;418 const one_plus_one: i32 = 1 + 1;
318 warn("1 + 1 = {}\n", .{one_plus_one});419 print("1 + 1 = {}\n", .{one_plus_one});
319420
320 // floats421 // floats
321 const seven_div_three: f32 = 7.0 / 3.0;422 const seven_div_three: f32 = 7.0 / 3.0;
322 warn("7.0 / 3.0 = {}\n", .{seven_div_three});423 print("7.0 / 3.0 = {}\n", .{seven_div_three});
323424
324 // boolean425 // boolean
325 warn("{}\n{}\n{}\n", .{426 print("{}\n{}\n{}\n", .{
326 true and false,427 true and false,
327 true or false,428 true or false,
328 !true,429 !true,
...@@ -332,7 +433,7 @@ pub fn main() void {...@@ -332,7 +433,7 @@ pub fn main() void {
332 var optional_value: ?[]const u8 = null;433 var optional_value: ?[]const u8 = null;
333 assert(optional_value == null);434 assert(optional_value == null);
334435
335 warn("\noptional 1\ntype: {}\nvalue: {}\n", .{436 print("\noptional 1\ntype: {}\nvalue: {}\n", .{
336 @typeName(@TypeOf(optional_value)),437 @typeName(@TypeOf(optional_value)),
337 optional_value,438 optional_value,
338 });439 });
...@@ -340,7 +441,7 @@ pub fn main() void {...@@ -340,7 +441,7 @@ pub fn main() void {
340 optional_value = "hi";441 optional_value = "hi";
341 assert(optional_value != null);442 assert(optional_value != null);
342443
343 warn("\noptional 2\ntype: {}\nvalue: {}\n", .{444 print("\noptional 2\ntype: {}\nvalue: {}\n", .{
344 @typeName(@TypeOf(optional_value)),445 @typeName(@TypeOf(optional_value)),
345 optional_value,446 optional_value,
346 });447 });
...@@ -348,14 +449,14 @@ pub fn main() void {...@@ -348,14 +449,14 @@ pub fn main() void {
348 // error union449 // error union
349 var number_or_error: anyerror!i32 = error.ArgNotFound;450 var number_or_error: anyerror!i32 = error.ArgNotFound;
350451
351 warn("\nerror union 1\ntype: {}\nvalue: {}\n", .{452 print("\nerror union 1\ntype: {}\nvalue: {}\n", .{
352 @typeName(@TypeOf(number_or_error)),453 @typeName(@TypeOf(number_or_error)),
353 number_or_error,454 number_or_error,
354 });455 });
355456
356 number_or_error = 1234;457 number_or_error = 1234;
357458
358 warn("\nerror union 2\ntype: {}\nvalue: {}\n", .{459 print("\nerror union 2\ntype: {}\nvalue: {}\n", .{
359 @typeName(@TypeOf(number_or_error)),460 @typeName(@TypeOf(number_or_error)),
360 number_or_error,461 number_or_error,
361 });462 });
...@@ -994,15 +1095,15 @@ export fn foo_optimized(x: f64) f64 {...@@ -994,15 +1095,15 @@ export fn foo_optimized(x: f64) f64 {
994 which operates in strict mode.</p>1095 which operates in strict mode.</p>
995 {#code_begin|exe|float_mode#}1096 {#code_begin|exe|float_mode#}
996 {#code_link_object|foo#}1097 {#code_link_object|foo#}
997const warn = @import("std").debug.warn;1098const print = @import("std").debug.print;
9981099
999extern fn foo_strict(x: f64) f64;1100extern fn foo_strict(x: f64) f64;
1000extern fn foo_optimized(x: f64) f64;1101extern fn foo_optimized(x: f64) f64;
10011102
1002pub fn main() void {1103pub fn main() void {
1003 const x = 0.001;1104 const x = 0.001;
1004 warn("optimized = {}\n", .{foo_optimized(x)});1105 print("optimized = {}\n", .{foo_optimized(x)});
1005 warn("strict = {}\n", .{foo_strict(x)});1106 print("strict = {}\n", .{foo_strict(x)});
1006}1107}
1007 {#code_end#}1108 {#code_end#}
1008 {#see_also|@setFloatMode|Division by Zero#}1109 {#see_also|@setFloatMode|Division by Zero#}
...@@ -1786,7 +1887,7 @@ test "fully anonymous list literal" {...@@ -1786,7 +1887,7 @@ test "fully anonymous list literal" {
1786 dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi"});1887 dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi"});
1787}1888}
17881889
1789fn dump(args: var) void {1890fn dump(args: anytype) void {
1790 assert(args.@"0" == 1234);1891 assert(args.@"0" == 1234);
1791 assert(args.@"1" == 12.34);1892 assert(args.@"1" == 12.34);
1792 assert(args.@"2");1893 assert(args.@"2");
...@@ -1849,7 +1950,7 @@ test "null terminated array" {...@@ -1849,7 +1950,7 @@ test "null terminated array" {
18491950
1850 {#header_open|Vectors#}1951 {#header_open|Vectors#}
1851 <p>1952 <p>
1852 A vector is a group of {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on1953 A vector is a group of booleans, {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on
1853 in parallel using a single instruction ({#link|SIMD#}). Vector types are created with the builtin function {#link|@Type#},1954 in parallel using a single instruction ({#link|SIMD#}). Vector types are created with the builtin function {#link|@Type#},
1854 or using the shorthand as {#syntax#}std.meta.Vector{#endsyntax#}.1955 or using the shorthand as {#syntax#}std.meta.Vector{#endsyntax#}.
1855 </p>1956 </p>
...@@ -2668,9 +2769,9 @@ const std = @import("std");...@@ -2668,9 +2769,9 @@ const std = @import("std");
26682769
2669pub fn main() void {2770pub fn main() void {
2670 const Foo = struct {};2771 const Foo = struct {};
2671 std.debug.warn("variable: {}\n", .{@typeName(Foo)});2772 std.debug.print("variable: {}\n", .{@typeName(Foo)});
2672 std.debug.warn("anonymous: {}\n", .{@typeName(struct {})});2773 std.debug.print("anonymous: {}\n", .{@typeName(struct {})});
2673 std.debug.warn("function: {}\n", .{@typeName(List(i32))});2774 std.debug.print("function: {}\n", .{@typeName(List(i32))});
2674}2775}
26752776
2676fn List(comptime T: type) type {2777fn List(comptime T: type) type {
...@@ -2718,7 +2819,7 @@ test "fully anonymous struct" {...@@ -2718,7 +2819,7 @@ test "fully anonymous struct" {
2718 });2819 });
2719}2820}
27202821
2721fn dump(args: var) void {2822fn dump(args: anytype) void {
2722 assert(args.int == 1234);2823 assert(args.int == 1234);
2723 assert(args.float == 12.34);2824 assert(args.float == 12.34);
2724 assert(args.b);2825 assert(args.b);
...@@ -3862,6 +3963,48 @@ test "if error union" {...@@ -3862,6 +3963,48 @@ test "if error union" {
3862 unreachable;3963 unreachable;
3863 }3964 }
3864}3965}
3966
3967test "if error union with optional" {
3968 // If expressions test for errors before unwrapping optionals.
3969 // The |optional_value| capture's type is ?u32.
3970
3971 const a: anyerror!?u32 = 0;
3972 if (a) |optional_value| {
3973 assert(optional_value.? == 0);
3974 } else |err| {
3975 unreachable;
3976 }
3977
3978 const b: anyerror!?u32 = null;
3979 if (b) |optional_value| {
3980 assert(optional_value == null);
3981 } else |err| {
3982 unreachable;
3983 }
3984
3985 const c: anyerror!?u32 = error.BadValue;
3986 if (c) |optional_value| {
3987 unreachable;
3988 } else |err| {
3989 assert(err == error.BadValue);
3990 }
3991
3992 // Access the value by reference by using a pointer capture each time.
3993 var d: anyerror!?u32 = 3;
3994 if (d) |*optional_value| {
3995 if (optional_value.*) |*value| {
3996 value.* = 9;
3997 }
3998 } else |err| {
3999 unreachable;
4000 }
4001
4002 if (d) |optional_value| {
4003 assert(optional_value.? == 9);
4004 } else |err| {
4005 unreachable;
4006 }
4007}
3865 {#code_end#}4008 {#code_end#}
3866 {#see_also|Optionals|Errors#}4009 {#see_also|Optionals|Errors#}
3867 {#header_close#}4010 {#header_close#}
...@@ -3869,7 +4012,7 @@ test "if error union" {...@@ -3869,7 +4012,7 @@ test "if error union" {
3869 {#code_begin|test|defer#}4012 {#code_begin|test|defer#}
3870const std = @import("std");4013const std = @import("std");
3871const assert = std.debug.assert;4014const assert = std.debug.assert;
3872const warn = std.debug.warn;4015const print = std.debug.print;
38734016
3874// defer will execute an expression at the end of the current scope.4017// defer will execute an expression at the end of the current scope.
3875fn deferExample() usize {4018fn deferExample() usize {
...@@ -3892,18 +4035,18 @@ test "defer basics" {...@@ -3892,18 +4035,18 @@ test "defer basics" {
3892// If multiple defer statements are specified, they will be executed in4035// If multiple defer statements are specified, they will be executed in
3893// the reverse order they were run.4036// the reverse order they were run.
3894fn deferUnwindExample() void {4037fn deferUnwindExample() void {
3895 warn("\n", .{});4038 print("\n", .{});
38964039
3897 defer {4040 defer {
3898 warn("1 ", .{});4041 print("1 ", .{});
3899 }4042 }
3900 defer {4043 defer {
3901 warn("2 ", .{});4044 print("2 ", .{});
3902 }4045 }
3903 if (false) {4046 if (false) {
3904 // defers are not run if they are never executed.4047 // defers are not run if they are never executed.
3905 defer {4048 defer {
3906 warn("3 ", .{});4049 print("3 ", .{});
3907 }4050 }
3908 }4051 }
3909}4052}
...@@ -3918,15 +4061,15 @@ test "defer unwinding" {...@@ -3918,15 +4061,15 @@ test "defer unwinding" {
3918// This is especially useful in allowing a function to clean up properly4061// This is especially useful in allowing a function to clean up properly
3919// on error, and replaces goto error handling tactics as seen in c.4062// on error, and replaces goto error handling tactics as seen in c.
3920fn deferErrorExample(is_error: bool) !void {4063fn deferErrorExample(is_error: bool) !void {
3921 warn("\nstart of function\n", .{});4064 print("\nstart of function\n", .{});
39224065
3923 // This will always be executed on exit4066 // This will always be executed on exit
3924 defer {4067 defer {
3925 warn("end of function\n", .{});4068 print("end of function\n", .{});
3926 }4069 }
39274070
3928 errdefer {4071 errdefer {
3929 warn("encountered an error!\n", .{});4072 print("encountered an error!\n", .{});
3930 }4073 }
39314074
3932 if (is_error) {4075 if (is_error) {
...@@ -4140,14 +4283,14 @@ test "pass struct to function" {...@@ -4140,14 +4283,14 @@ test "pass struct to function" {
4140 {#header_close#}4283 {#header_close#}
4141 {#header_open|Function Parameter Type Inference#}4284 {#header_open|Function Parameter Type Inference#}
4142 <p>4285 <p>
4143 Function parameters can be declared with {#syntax#}var{#endsyntax#} in place of the type.4286 Function parameters can be declared with {#syntax#}anytype{#endsyntax#} in place of the type.
4144 In this case the parameter types will be inferred when the function is called.4287 In this case the parameter types will be inferred when the function is called.
4145 Use {#link|@TypeOf#} and {#link|@typeInfo#} to get information about the inferred type.4288 Use {#link|@TypeOf#} and {#link|@typeInfo#} to get information about the inferred type.
4146 </p>4289 </p>
4147 {#code_begin|test#}4290 {#code_begin|test#}
4148const assert = @import("std").debug.assert;4291const assert = @import("std").debug.assert;
41494292
4150fn addFortyTwo(x: var) @TypeOf(x) {4293fn addFortyTwo(x: anytype) @TypeOf(x) {
4151 return x + 42;4294 return x + 42;
4152}4295}
41534296
...@@ -5364,11 +5507,11 @@ const std = @import("std");...@@ -5364,11 +5507,11 @@ const std = @import("std");
5364const assert = std.debug.assert;5507const assert = std.debug.assert;
53655508
5366test "turn HashMap into a set with void" {5509test "turn HashMap into a set with void" {
5367 var map = std.HashMap(i32, void, hash_i32, eql_i32).init(std.testing.allocator);5510 var map = std.AutoHashMap(i32, void).init(std.testing.allocator);
5368 defer map.deinit();5511 defer map.deinit();
53695512
5370 _ = try map.put(1, {});5513 try map.put(1, {});
5371 _ = try map.put(2, {});5514 try map.put(2, {});
53725515
5373 assert(map.contains(2));5516 assert(map.contains(2));
5374 assert(!map.contains(3));5517 assert(!map.contains(3));
...@@ -5376,14 +5519,6 @@ test "turn HashMap into a set with void" {...@@ -5376,14 +5519,6 @@ test "turn HashMap into a set with void" {
5376 _ = map.remove(2);5519 _ = map.remove(2);
5377 assert(!map.contains(2));5520 assert(!map.contains(2));
5378}5521}
5379
5380fn hash_i32(x: i32) u32 {
5381 return @bitCast(u32, x);
5382}
5383
5384fn eql_i32(a: i32, b: i32) bool {
5385 return a == b;
5386}
5387 {#code_end#}5522 {#code_end#}
5388 <p>Note that this is different from using a dummy value for the hash map value.5523 <p>Note that this is different from using a dummy value for the hash map value.
5389 By using {#syntax#}void{#endsyntax#} as the type of the value, the hash map entry type has no value field, and5524 By using {#syntax#}void{#endsyntax#} as the type of the value, the hash map entry type has no value field, and
...@@ -5925,13 +6060,13 @@ const Node = struct {...@@ -5925,13 +6060,13 @@ const Node = struct {
5925 Putting all of this together, let's see how {#syntax#}printf{#endsyntax#} works in Zig.6060 Putting all of this together, let's see how {#syntax#}printf{#endsyntax#} works in Zig.
5926 </p>6061 </p>
5927 {#code_begin|exe|printf#}6062 {#code_begin|exe|printf#}
5928const warn = @import("std").debug.warn;6063const print = @import("std").debug.print;
59296064
5930const a_number: i32 = 1234;6065const a_number: i32 = 1234;
5931const a_string = "foobar";6066const a_string = "foobar";
59326067
5933pub fn main() void {6068pub fn main() void {
5934 warn("here is a string: '{}' here is a number: {}\n", .{a_string, a_number});6069 print("here is a string: '{}' here is a number: {}\n", .{a_string, a_number});
5935}6070}
5936 {#code_end#}6071 {#code_end#}
59376072
...@@ -5941,7 +6076,7 @@ pub fn main() void {...@@ -5941,7 +6076,7 @@ pub fn main() void {
59416076
5942 {#code_begin|syntax#}6077 {#code_begin|syntax#}
5943/// Calls print and then flushes the buffer.6078/// Calls print and then flushes the buffer.
5944pub fn printf(self: *OutStream, comptime format: []const u8, args: var) anyerror!void {6079pub fn printf(self: *OutStream, comptime format: []const u8, args: anytype) anyerror!void {
5945 const State = enum {6080 const State = enum {
5946 Start,6081 Start,
5947 OpenBrace,6082 OpenBrace,
...@@ -6027,7 +6162,7 @@ pub fn printf(self: *OutStream, arg0: i32, arg1: []const u8) !void {...@@ -6027,7 +6162,7 @@ pub fn printf(self: *OutStream, arg0: i32, arg1: []const u8) !void {
6027 on the type:6162 on the type:
6028 </p>6163 </p>
6029 {#code_begin|syntax#}6164 {#code_begin|syntax#}
6030pub fn printValue(self: *OutStream, value: var) !void {6165pub fn printValue(self: *OutStream, value: anytype) !void {
6031 switch (@typeInfo(@TypeOf(value))) {6166 switch (@typeInfo(@TypeOf(value))) {
6032 .Int => {6167 .Int => {
6033 return self.printInt(T, value);6168 return self.printInt(T, value);
...@@ -6045,13 +6180,13 @@ pub fn printValue(self: *OutStream, value: var) !void {...@@ -6045,13 +6180,13 @@ pub fn printValue(self: *OutStream, value: var) !void {
6045 And now, what happens if we give too many arguments to {#syntax#}printf{#endsyntax#}?6180 And now, what happens if we give too many arguments to {#syntax#}printf{#endsyntax#}?
6046 </p>6181 </p>
6047 {#code_begin|test_err|Unused arguments#}6182 {#code_begin|test_err|Unused arguments#}
6048const warn = @import("std").debug.warn;6183const print = @import("std").debug.print;
60496184
6050const a_number: i32 = 1234;6185const a_number: i32 = 1234;
6051const a_string = "foobar";6186const a_string = "foobar";
60526187
6053test "printf too many arguments" {6188test "printf too many arguments" {
6054 warn("here is a string: '{}' here is a number: {}\n", .{6189 print("here is a string: '{}' here is a number: {}\n", .{
6055 a_string,6190 a_string,
6056 a_number,6191 a_number,
6057 a_number,6192 a_number,
...@@ -6066,14 +6201,14 @@ test "printf too many arguments" {...@@ -6066,14 +6201,14 @@ test "printf too many arguments" {
6066 only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}:6201 only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}:
6067 </p>6202 </p>
6068 {#code_begin|exe|printf#}6203 {#code_begin|exe|printf#}
6069const warn = @import("std").debug.warn;6204const print = @import("std").debug.print;
60706205
6071const a_number: i32 = 1234;6206const a_number: i32 = 1234;
6072const a_string = "foobar";6207const a_string = "foobar";
6073const fmt = "here is a string: '{}' here is a number: {}\n";6208const fmt = "here is a string: '{}' here is a number: {}\n";
60746209
6075pub fn main() void {6210pub fn main() void {
6076 warn(fmt, .{a_string, a_number});6211 print(fmt, .{a_string, a_number});
6077}6212}
6078 {#code_end#}6213 {#code_end#}
6079 <p>6214 <p>
...@@ -6511,7 +6646,7 @@ pub fn main() void {...@@ -6511,7 +6646,7 @@ pub fn main() void {
65116646
6512fn amainWrap() void {6647fn amainWrap() void {
6513 amain() catch |e| {6648 amain() catch |e| {
6514 std.debug.warn("{}\n", .{e});6649 std.debug.print("{}\n", .{e});
6515 if (@errorReturnTrace()) |trace| {6650 if (@errorReturnTrace()) |trace| {
6516 std.debug.dumpStackTrace(trace.*);6651 std.debug.dumpStackTrace(trace.*);
6517 }6652 }
...@@ -6541,8 +6676,8 @@ fn amain() !void {...@@ -6541,8 +6676,8 @@ fn amain() !void {
6541 const download_text = try await download_frame;6676 const download_text = try await download_frame;
6542 defer allocator.free(download_text);6677 defer allocator.free(download_text);
65436678
6544 std.debug.warn("download_text: {}\n", .{download_text});6679 std.debug.print("download_text: {}\n", .{download_text});
6545 std.debug.warn("file_text: {}\n", .{file_text});6680 std.debug.print("file_text: {}\n", .{file_text});
6546}6681}
65476682
6548var global_download_frame: anyframe = undefined;6683var global_download_frame: anyframe = undefined;
...@@ -6552,7 +6687,7 @@ fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 {...@@ -6552,7 +6687,7 @@ fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 {
6552 suspend {6687 suspend {
6553 global_download_frame = @frame();6688 global_download_frame = @frame();
6554 }6689 }
6555 std.debug.warn("fetchUrl returning\n", .{});6690 std.debug.print("fetchUrl returning\n", .{});
6556 return result;6691 return result;
6557}6692}
65586693
...@@ -6563,7 +6698,7 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {...@@ -6563,7 +6698,7 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
6563 suspend {6698 suspend {
6564 global_file_frame = @frame();6699 global_file_frame = @frame();
6565 }6700 }
6566 std.debug.warn("readFile returning\n", .{});6701 std.debug.print("readFile returning\n", .{});
6567 return result;6702 return result;
6568}6703}
6569 {#code_end#}6704 {#code_end#}
...@@ -6581,7 +6716,7 @@ pub fn main() void {...@@ -6581,7 +6716,7 @@ pub fn main() void {
65816716
6582fn amainWrap() void {6717fn amainWrap() void {
6583 amain() catch |e| {6718 amain() catch |e| {
6584 std.debug.warn("{}\n", .{e});6719 std.debug.print("{}\n", .{e});
6585 if (@errorReturnTrace()) |trace| {6720 if (@errorReturnTrace()) |trace| {
6586 std.debug.dumpStackTrace(trace.*);6721 std.debug.dumpStackTrace(trace.*);
6587 }6722 }
...@@ -6611,21 +6746,21 @@ fn amain() !void {...@@ -6611,21 +6746,21 @@ fn amain() !void {
6611 const download_text = try await download_frame;6746 const download_text = try await download_frame;
6612 defer allocator.free(download_text);6747 defer allocator.free(download_text);
66136748
6614 std.debug.warn("download_text: {}\n", .{download_text});6749 std.debug.print("download_text: {}\n", .{download_text});
6615 std.debug.warn("file_text: {}\n", .{file_text});6750 std.debug.print("file_text: {}\n", .{file_text});
6616}6751}
66176752
6618fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 {6753fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 {
6619 const result = try std.mem.dupe(allocator, u8, "this is the downloaded url contents");6754 const result = try std.mem.dupe(allocator, u8, "this is the downloaded url contents");
6620 errdefer allocator.free(result);6755 errdefer allocator.free(result);
6621 std.debug.warn("fetchUrl returning\n", .{});6756 std.debug.print("fetchUrl returning\n", .{});
6622 return result;6757 return result;
6623}6758}
66246759
6625fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {6760fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
6626 const result = try std.mem.dupe(allocator, u8, "this is the file contents");6761 const result = try std.mem.dupe(allocator, u8, "this is the file contents");
6627 errdefer allocator.free(result);6762 errdefer allocator.free(result);
6628 std.debug.warn("readFile returning\n", .{});6763 std.debug.print("readFile returning\n", .{});
6629 return result;6764 return result;
6630}6765}
6631 {#code_end#}6766 {#code_end#}
...@@ -6653,7 +6788,7 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {...@@ -6653,7 +6788,7 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
6653 </p>6788 </p>
6654 {#header_close#}6789 {#header_close#}
6655 {#header_open|@alignCast#}6790 {#header_open|@alignCast#}
6656 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: var) var{#endsyntax#}</pre>6791 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}</pre>
6657 <p>6792 <p>
6658 {#syntax#}ptr{#endsyntax#} can be {#syntax#}*T{#endsyntax#}, {#syntax#}fn(){#endsyntax#}, {#syntax#}?*T{#endsyntax#},6793 {#syntax#}ptr{#endsyntax#} can be {#syntax#}*T{#endsyntax#}, {#syntax#}fn(){#endsyntax#}, {#syntax#}?*T{#endsyntax#},
6659 {#syntax#}?fn(){#endsyntax#}, or {#syntax#}[]T{#endsyntax#}. It returns the same type as {#syntax#}ptr{#endsyntax#}6794 {#syntax#}?fn(){#endsyntax#}, or {#syntax#}[]T{#endsyntax#}. It returns the same type as {#syntax#}ptr{#endsyntax#}
...@@ -6690,7 +6825,7 @@ comptime {...@@ -6690,7 +6825,7 @@ comptime {
6690 {#header_close#}6825 {#header_close#}
66916826
6692 {#header_open|@asyncCall#}6827 {#header_open|@asyncCall#}
6693 <pre>{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: ...) anyframe->T{#endsyntax#}</pre>6828 <pre>{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: anytype) anyframe->T{#endsyntax#}</pre>
6694 <p>6829 <p>
6695 {#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,6830 {#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,
6696 which may or may not be an {#link|async function|Async Functions#}.6831 which may or may not be an {#link|async function|Async Functions#}.
...@@ -6717,7 +6852,7 @@ test "async fn pointer in a struct field" {...@@ -6717,7 +6852,7 @@ test "async fn pointer in a struct field" {
6717 };6852 };
6718 var foo = Foo{ .bar = func };6853 var foo = Foo{ .bar = func };
6719 var bytes: [64]u8 align(@alignOf(@Frame(func))) = undefined;6854 var bytes: [64]u8 align(@alignOf(@Frame(func))) = undefined;
6720 const f = @asyncCall(&bytes, {}, foo.bar, &data);6855 const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
6721 assert(data == 2);6856 assert(data == 2);
6722 resume f;6857 resume f;
6723 assert(data == 4);6858 assert(data == 4);
...@@ -6778,7 +6913,7 @@ fn func(y: *i32) void {...@@ -6778,7 +6913,7 @@ fn func(y: *i32) void {
6778 </p>6913 </p>
6779 {#header_close#}6914 {#header_close#}
6780 {#header_open|@bitCast#}6915 {#header_open|@bitCast#}
6781 <pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>6916 <pre>{#syntax#}@bitCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre>
6782 <p>6917 <p>
6783 Converts a value of one type to another type.6918 Converts a value of one type to another type.
6784 </p>6919 </p>
...@@ -6899,7 +7034,7 @@ fn func(y: *i32) void {...@@ -6899,7 +7034,7 @@ fn func(y: *i32) void {
6899 {#header_close#}7034 {#header_close#}
69007035
6901 {#header_open|@call#}7036 {#header_open|@call#}
6902 <pre>{#syntax#}@call(options: std.builtin.CallOptions, function: var, args: var) var{#endsyntax#}</pre>7037 <pre>{#syntax#}@call(options: std.builtin.CallOptions, function: anytype, args: anytype) anytype{#endsyntax#}</pre>
6903 <p>7038 <p>
6904 Calls a function, in the same way that invoking an expression with parentheses does:7039 Calls a function, in the same way that invoking an expression with parentheses does:
6905 </p>7040 </p>
...@@ -7121,7 +7256,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -7121,7 +7256,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
7121 compile-time executing code.7256 compile-time executing code.
7122 </p>7257 </p>
7123 {#code_begin|test_err|found compile log statement#}7258 {#code_begin|test_err|found compile log statement#}
7124const warn = @import("std").debug.warn;7259const print = @import("std").debug.print;
71257260
7126const num1 = blk: {7261const num1 = blk: {
7127 var val1: i32 = 99;7262 var val1: i32 = 99;
...@@ -7133,7 +7268,7 @@ const num1 = blk: {...@@ -7133,7 +7268,7 @@ const num1 = blk: {
7133test "main" {7268test "main" {
7134 @compileLog("comptime in main");7269 @compileLog("comptime in main");
71357270
7136 warn("Runtime in main, num1 = {}.\n", .{num1});7271 print("Runtime in main, num1 = {}.\n", .{num1});
7137}7272}
7138 {#code_end#}7273 {#code_end#}
7139 <p>7274 <p>
...@@ -7145,7 +7280,7 @@ test "main" {...@@ -7145,7 +7280,7 @@ test "main" {
7145 program compiles successfully and the generated executable prints:7280 program compiles successfully and the generated executable prints:
7146 </p>7281 </p>
7147 {#code_begin|test#}7282 {#code_begin|test#}
7148const warn = @import("std").debug.warn;7283const print = @import("std").debug.print;
71497284
7150const num1 = blk: {7285const num1 = blk: {
7151 var val1: i32 = 99;7286 var val1: i32 = 99;
...@@ -7154,7 +7289,7 @@ const num1 = blk: {...@@ -7154,7 +7289,7 @@ const num1 = blk: {
7154};7289};
71557290
7156test "main" {7291test "main" {
7157 warn("Runtime in main, num1 = {}.\n", .{num1});7292 print("Runtime in main, num1 = {}.\n", .{num1});
7158}7293}
7159 {#code_end#}7294 {#code_end#}
7160 {#header_close#}7295 {#header_close#}
...@@ -7246,7 +7381,7 @@ test "main" {...@@ -7246,7 +7381,7 @@ test "main" {
7246 {#header_close#}7381 {#header_close#}
72477382
7248 {#header_open|@enumToInt#}7383 {#header_open|@enumToInt#}
7249 <pre>{#syntax#}@enumToInt(enum_or_tagged_union: var) var{#endsyntax#}</pre>7384 <pre>{#syntax#}@enumToInt(enum_or_tagged_union: anytype) anytype{#endsyntax#}</pre>
7250 <p>7385 <p>
7251 Converts an enumeration value into its integer tag type. When a tagged union is passed,7386 Converts an enumeration value into its integer tag type. When a tagged union is passed,
7252 the tag value is used as the enumeration value.7387 the tag value is used as the enumeration value.
...@@ -7281,7 +7416,7 @@ test "main" {...@@ -7281,7 +7416,7 @@ test "main" {
7281 {#header_close#}7416 {#header_close#}
72827417
7283 {#header_open|@errorToInt#}7418 {#header_open|@errorToInt#}
7284 <pre>{#syntax#}@errorToInt(err: var) std.meta.IntType(false, @sizeOf(anyerror) * 8){#endsyntax#}</pre>7419 <pre>{#syntax#}@errorToInt(err: anytype) std.meta.IntType(false, @sizeOf(anyerror) * 8){#endsyntax#}</pre>
7285 <p>7420 <p>
7286 Supports the following types:7421 Supports the following types:
7287 </p>7422 </p>
...@@ -7301,7 +7436,7 @@ test "main" {...@@ -7301,7 +7436,7 @@ test "main" {
7301 {#header_close#}7436 {#header_close#}
73027437
7303 {#header_open|@errSetCast#}7438 {#header_open|@errSetCast#}
7304 <pre>{#syntax#}@errSetCast(comptime T: DestType, value: var) DestType{#endsyntax#}</pre>7439 <pre>{#syntax#}@errSetCast(comptime T: DestType, value: anytype) DestType{#endsyntax#}</pre>
7305 <p>7440 <p>
7306 Converts an error value from one error set to another error set. Attempting to convert an error7441 Converts an error value from one error set to another error set. Attempting to convert an error
7307 which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}.7442 which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}.
...@@ -7309,7 +7444,7 @@ test "main" {...@@ -7309,7 +7444,7 @@ test "main" {
7309 {#header_close#}7444 {#header_close#}
73107445
7311 {#header_open|@export#}7446 {#header_open|@export#}
7312 <pre>{#syntax#}@export(target: var, comptime options: std.builtin.ExportOptions) void{#endsyntax#}</pre>7447 <pre>{#syntax#}@export(target: anytype, comptime options: std.builtin.ExportOptions) void{#endsyntax#}</pre>
7313 <p>7448 <p>
7314 Creates a symbol in the output object file.7449 Creates a symbol in the output object file.
7315 </p>7450 </p>
...@@ -7354,7 +7489,7 @@ export fn @"A function name that is a complete sentence."() void {}...@@ -7354,7 +7489,7 @@ export fn @"A function name that is a complete sentence."() void {}
7354 {#header_close#}7489 {#header_close#}
73557490
7356 {#header_open|@field#}7491 {#header_open|@field#}
7357 <pre>{#syntax#}@field(lhs: var, comptime field_name: []const u8) (field){#endsyntax#}</pre>7492 <pre>{#syntax#}@field(lhs: anytype, comptime field_name: []const u8) (field){#endsyntax#}</pre>
7358 <p>Performs field access by a compile-time string.7493 <p>Performs field access by a compile-time string.
7359 </p>7494 </p>
7360 {#code_begin|test#}7495 {#code_begin|test#}
...@@ -7388,7 +7523,7 @@ test "field access by string" {...@@ -7388,7 +7523,7 @@ test "field access by string" {
7388 {#header_close#}7523 {#header_close#}
73897524
7390 {#header_open|@floatCast#}7525 {#header_open|@floatCast#}
7391 <pre>{#syntax#}@floatCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>7526 <pre>{#syntax#}@floatCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre>
7392 <p>7527 <p>
7393 Convert from one float type to another. This cast is safe, but may cause the7528 Convert from one float type to another. This cast is safe, but may cause the
7394 numeric value to lose precision.7529 numeric value to lose precision.
...@@ -7396,7 +7531,7 @@ test "field access by string" {...@@ -7396,7 +7531,7 @@ test "field access by string" {
7396 {#header_close#}7531 {#header_close#}
73977532
7398 {#header_open|@floatToInt#}7533 {#header_open|@floatToInt#}
7399 <pre>{#syntax#}@floatToInt(comptime DestType: type, float: var) DestType{#endsyntax#}</pre>7534 <pre>{#syntax#}@floatToInt(comptime DestType: type, float: anytype) DestType{#endsyntax#}</pre>
7400 <p>7535 <p>
7401 Converts the integer part of a floating point number to the destination type.7536 Converts the integer part of a floating point number to the destination type.
7402 </p>7537 </p>
...@@ -7422,7 +7557,7 @@ test "field access by string" {...@@ -7422,7 +7557,7 @@ test "field access by string" {
7422 {#header_close#}7557 {#header_close#}
74237558
7424 {#header_open|@Frame#}7559 {#header_open|@Frame#}
7425 <pre>{#syntax#}@Frame(func: var) type{#endsyntax#}</pre>7560 <pre>{#syntax#}@Frame(func: anytype) type{#endsyntax#}</pre>
7426 <p>7561 <p>
7427 This function returns the frame type of a function. This works for {#link|Async Functions#}7562 This function returns the frame type of a function. This works for {#link|Async Functions#}
7428 as well as any function without a specific calling convention.7563 as well as any function without a specific calling convention.
...@@ -7531,7 +7666,7 @@ test "@hasDecl" {...@@ -7531,7 +7666,7 @@ test "@hasDecl" {
7531 source file than the one they are declared in.7666 source file than the one they are declared in.
7532 </p>7667 </p>
7533 <p>7668 <p>
7534 {#syntax#}path{#endsyntax#} can be a relative or absolute path, or it can be the name of a package.7669 {#syntax#}path{#endsyntax#} can be a relative path or it can be the name of a package.
7535 If it is a relative path, it is relative to the file that contains the {#syntax#}@import{#endsyntax#}7670 If it is a relative path, it is relative to the file that contains the {#syntax#}@import{#endsyntax#}
7536 function call.7671 function call.
7537 </p>7672 </p>
...@@ -7548,7 +7683,7 @@ test "@hasDecl" {...@@ -7548,7 +7683,7 @@ test "@hasDecl" {
7548 {#header_close#}7683 {#header_close#}
75497684
7550 {#header_open|@intCast#}7685 {#header_open|@intCast#}
7551 <pre>{#syntax#}@intCast(comptime DestType: type, int: var) DestType{#endsyntax#}</pre>7686 <pre>{#syntax#}@intCast(comptime DestType: type, int: anytype) DestType{#endsyntax#}</pre>
7552 <p>7687 <p>
7553 Converts an integer to another integer while keeping the same numerical value.7688 Converts an integer to another integer while keeping the same numerical value.
7554 Attempting to convert a number which is out of range of the destination type results in7689 Attempting to convert a number which is out of range of the destination type results in
...@@ -7589,7 +7724,7 @@ test "@hasDecl" {...@@ -7589,7 +7724,7 @@ test "@hasDecl" {
7589 {#header_close#}7724 {#header_close#}
75907725
7591 {#header_open|@intToFloat#}7726 {#header_open|@intToFloat#}
7592 <pre>{#syntax#}@intToFloat(comptime DestType: type, int: var) DestType{#endsyntax#}</pre>7727 <pre>{#syntax#}@intToFloat(comptime DestType: type, int: anytype) DestType{#endsyntax#}</pre>
7593 <p>7728 <p>
7594 Converts an integer to the closest floating point representation. To convert the other way, use {#link|@floatToInt#}. This cast is always safe.7729 Converts an integer to the closest floating point representation. To convert the other way, use {#link|@floatToInt#}. This cast is always safe.
7595 </p>7730 </p>
...@@ -7740,7 +7875,7 @@ test "@wasmMemoryGrow" {...@@ -7740,7 +7875,7 @@ test "@wasmMemoryGrow" {
7740 {#header_close#}7875 {#header_close#}
77417876
7742 {#header_open|@ptrCast#}7877 {#header_open|@ptrCast#}
7743 <pre>{#syntax#}@ptrCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>7878 <pre>{#syntax#}@ptrCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre>
7744 <p>7879 <p>
7745 Converts a pointer of one type to a pointer of another type.7880 Converts a pointer of one type to a pointer of another type.
7746 </p>7881 </p>
...@@ -7751,7 +7886,7 @@ test "@wasmMemoryGrow" {...@@ -7751,7 +7886,7 @@ test "@wasmMemoryGrow" {
7751 {#header_close#}7886 {#header_close#}
77527887
7753 {#header_open|@ptrToInt#}7888 {#header_open|@ptrToInt#}
7754 <pre>{#syntax#}@ptrToInt(value: var) usize{#endsyntax#}</pre>7889 <pre>{#syntax#}@ptrToInt(value: anytype) usize{#endsyntax#}</pre>
7755 <p>7890 <p>
7756 Converts {#syntax#}value{#endsyntax#} to a {#syntax#}usize{#endsyntax#} which is the address of the pointer. {#syntax#}value{#endsyntax#} can be one of these types:7891 Converts {#syntax#}value{#endsyntax#} to a {#syntax#}usize{#endsyntax#} which is the address of the pointer. {#syntax#}value{#endsyntax#} can be one of these types:
7757 </p>7892 </p>
...@@ -8009,7 +8144,7 @@ test "@setRuntimeSafety" {...@@ -8009,7 +8144,7 @@ test "@setRuntimeSafety" {
8009 {#header_close#}8144 {#header_close#}
80108145
8011 {#header_open|@splat#}8146 {#header_open|@splat#}
8012 <pre>{#syntax#}@splat(comptime len: u32, scalar: var) std.meta.Vector(len, @TypeOf(scalar)){#endsyntax#}</pre>8147 <pre>{#syntax#}@splat(comptime len: u32, scalar: anytype) std.meta.Vector(len, @TypeOf(scalar)){#endsyntax#}</pre>
8013 <p>8148 <p>
8014 Produces a vector of length {#syntax#}len{#endsyntax#} where each element is the value8149 Produces a vector of length {#syntax#}len{#endsyntax#} where each element is the value
8015 {#syntax#}scalar{#endsyntax#}:8150 {#syntax#}scalar{#endsyntax#}:
...@@ -8031,9 +8166,31 @@ test "vector @splat" {...@@ -8031,9 +8166,31 @@ test "vector @splat" {
8031 </p>8166 </p>
8032 {#see_also|Vectors|@shuffle#}8167 {#see_also|Vectors|@shuffle#}
8033 {#header_close#}8168 {#header_close#}
8169 {#header_open|@src#}
8170 <pre>{#syntax#}@src() std.builtin.SourceLocation{#endsyntax#}</pre>
8171 <p>
8172 Returns a {#syntax#}SourceLocation{#endsyntax#} struct representing the function's name and location in the source code. This must be called in a function.
8173 </p>
8174 {#code_begin|test#}
8175const std = @import("std");
8176const expect = std.testing.expect;
80348177
8178test "@src" {
8179 doTheTest();
8180}
8181
8182fn doTheTest() void {
8183 const src = @src();
8184
8185 expect(src.line == 9);
8186 expect(src.column == 17);
8187 expect(std.mem.endsWith(u8, src.fn_name, "doTheTest"));
8188 expect(std.mem.endsWith(u8, src.file, "test.zig"));
8189}
8190 {#code_end#}
8191 {#header_close#}
8035 {#header_open|@sqrt#}8192 {#header_open|@sqrt#}
8036 <pre>{#syntax#}@sqrt(value: var) @TypeOf(value){#endsyntax#}</pre>8193 <pre>{#syntax#}@sqrt(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8037 <p>8194 <p>
8038 Performs the square root of a floating point number. Uses a dedicated hardware instruction8195 Performs the square root of a floating point number. Uses a dedicated hardware instruction
8039 when available.8196 when available.
...@@ -8044,7 +8201,7 @@ test "vector @splat" {...@@ -8044,7 +8201,7 @@ test "vector @splat" {
8044 </p>8201 </p>
8045 {#header_close#}8202 {#header_close#}
8046 {#header_open|@sin#}8203 {#header_open|@sin#}
8047 <pre>{#syntax#}@sin(value: var) @TypeOf(value){#endsyntax#}</pre>8204 <pre>{#syntax#}@sin(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8048 <p>8205 <p>
8049 Sine trigometric function on a floating point number. Uses a dedicated hardware instruction8206 Sine trigometric function on a floating point number. Uses a dedicated hardware instruction
8050 when available.8207 when available.
...@@ -8055,7 +8212,7 @@ test "vector @splat" {...@@ -8055,7 +8212,7 @@ test "vector @splat" {
8055 </p>8212 </p>
8056 {#header_close#}8213 {#header_close#}
8057 {#header_open|@cos#}8214 {#header_open|@cos#}
8058 <pre>{#syntax#}@cos(value: var) @TypeOf(value){#endsyntax#}</pre>8215 <pre>{#syntax#}@cos(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8059 <p>8216 <p>
8060 Cosine trigometric function on a floating point number. Uses a dedicated hardware instruction8217 Cosine trigometric function on a floating point number. Uses a dedicated hardware instruction
8061 when available.8218 when available.
...@@ -8066,7 +8223,7 @@ test "vector @splat" {...@@ -8066,7 +8223,7 @@ test "vector @splat" {
8066 </p>8223 </p>
8067 {#header_close#}8224 {#header_close#}
8068 {#header_open|@exp#}8225 {#header_open|@exp#}
8069 <pre>{#syntax#}@exp(value: var) @TypeOf(value){#endsyntax#}</pre>8226 <pre>{#syntax#}@exp(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8070 <p>8227 <p>
8071 Base-e exponential function on a floating point number. Uses a dedicated hardware instruction8228 Base-e exponential function on a floating point number. Uses a dedicated hardware instruction
8072 when available.8229 when available.
...@@ -8077,7 +8234,7 @@ test "vector @splat" {...@@ -8077,7 +8234,7 @@ test "vector @splat" {
8077 </p>8234 </p>
8078 {#header_close#}8235 {#header_close#}
8079 {#header_open|@exp2#}8236 {#header_open|@exp2#}
8080 <pre>{#syntax#}@exp2(value: var) @TypeOf(value){#endsyntax#}</pre>8237 <pre>{#syntax#}@exp2(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8081 <p>8238 <p>
8082 Base-2 exponential function on a floating point number. Uses a dedicated hardware instruction8239 Base-2 exponential function on a floating point number. Uses a dedicated hardware instruction
8083 when available.8240 when available.
...@@ -8088,7 +8245,7 @@ test "vector @splat" {...@@ -8088,7 +8245,7 @@ test "vector @splat" {
8088 </p>8245 </p>
8089 {#header_close#}8246 {#header_close#}
8090 {#header_open|@log#}8247 {#header_open|@log#}
8091 <pre>{#syntax#}@log(value: var) @TypeOf(value){#endsyntax#}</pre>8248 <pre>{#syntax#}@log(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8092 <p>8249 <p>
8093 Returns the natural logarithm of a floating point number. Uses a dedicated hardware instruction8250 Returns the natural logarithm of a floating point number. Uses a dedicated hardware instruction
8094 when available.8251 when available.
...@@ -8099,7 +8256,7 @@ test "vector @splat" {...@@ -8099,7 +8256,7 @@ test "vector @splat" {
8099 </p>8256 </p>
8100 {#header_close#}8257 {#header_close#}
8101 {#header_open|@log2#}8258 {#header_open|@log2#}
8102 <pre>{#syntax#}@log2(value: var) @TypeOf(value){#endsyntax#}</pre>8259 <pre>{#syntax#}@log2(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8103 <p>8260 <p>
8104 Returns the logarithm to the base 2 of a floating point number. Uses a dedicated hardware instruction8261 Returns the logarithm to the base 2 of a floating point number. Uses a dedicated hardware instruction
8105 when available.8262 when available.
...@@ -8110,7 +8267,7 @@ test "vector @splat" {...@@ -8110,7 +8267,7 @@ test "vector @splat" {
8110 </p>8267 </p>
8111 {#header_close#}8268 {#header_close#}
8112 {#header_open|@log10#}8269 {#header_open|@log10#}
8113 <pre>{#syntax#}@log10(value: var) @TypeOf(value){#endsyntax#}</pre>8270 <pre>{#syntax#}@log10(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8114 <p>8271 <p>
8115 Returns the logarithm to the base 10 of a floating point number. Uses a dedicated hardware instruction8272 Returns the logarithm to the base 10 of a floating point number. Uses a dedicated hardware instruction
8116 when available.8273 when available.
...@@ -8121,7 +8278,7 @@ test "vector @splat" {...@@ -8121,7 +8278,7 @@ test "vector @splat" {
8121 </p>8278 </p>
8122 {#header_close#}8279 {#header_close#}
8123 {#header_open|@fabs#}8280 {#header_open|@fabs#}
8124 <pre>{#syntax#}@fabs(value: var) @TypeOf(value){#endsyntax#}</pre>8281 <pre>{#syntax#}@fabs(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8125 <p>8282 <p>
8126 Returns the absolute value of a floating point number. Uses a dedicated hardware instruction8283 Returns the absolute value of a floating point number. Uses a dedicated hardware instruction
8127 when available.8284 when available.
...@@ -8132,7 +8289,7 @@ test "vector @splat" {...@@ -8132,7 +8289,7 @@ test "vector @splat" {
8132 </p>8289 </p>
8133 {#header_close#}8290 {#header_close#}
8134 {#header_open|@floor#}8291 {#header_open|@floor#}
8135 <pre>{#syntax#}@floor(value: var) @TypeOf(value){#endsyntax#}</pre>8292 <pre>{#syntax#}@floor(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8136 <p>8293 <p>
8137 Returns the largest integral value not greater than the given floating point number.8294 Returns the largest integral value not greater than the given floating point number.
8138 Uses a dedicated hardware instruction when available.8295 Uses a dedicated hardware instruction when available.
...@@ -8143,7 +8300,7 @@ test "vector @splat" {...@@ -8143,7 +8300,7 @@ test "vector @splat" {
8143 </p>8300 </p>
8144 {#header_close#}8301 {#header_close#}
8145 {#header_open|@ceil#}8302 {#header_open|@ceil#}
8146 <pre>{#syntax#}@ceil(value: var) @TypeOf(value){#endsyntax#}</pre>8303 <pre>{#syntax#}@ceil(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8147 <p>8304 <p>
8148 Returns the largest integral value not less than the given floating point number.8305 Returns the largest integral value not less than the given floating point number.
8149 Uses a dedicated hardware instruction when available.8306 Uses a dedicated hardware instruction when available.
...@@ -8154,7 +8311,7 @@ test "vector @splat" {...@@ -8154,7 +8311,7 @@ test "vector @splat" {
8154 </p>8311 </p>
8155 {#header_close#}8312 {#header_close#}
8156 {#header_open|@trunc#}8313 {#header_open|@trunc#}
8157 <pre>{#syntax#}@trunc(value: var) @TypeOf(value){#endsyntax#}</pre>8314 <pre>{#syntax#}@trunc(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8158 <p>8315 <p>
8159 Rounds the given floating point number to an integer, towards zero.8316 Rounds the given floating point number to an integer, towards zero.
8160 Uses a dedicated hardware instruction when available.8317 Uses a dedicated hardware instruction when available.
...@@ -8165,7 +8322,7 @@ test "vector @splat" {...@@ -8165,7 +8322,7 @@ test "vector @splat" {
8165 </p>8322 </p>
8166 {#header_close#}8323 {#header_close#}
8167 {#header_open|@round#}8324 {#header_open|@round#}
8168 <pre>{#syntax#}@round(value: var) @TypeOf(value){#endsyntax#}</pre>8325 <pre>{#syntax#}@round(value: anytype) @TypeOf(value){#endsyntax#}</pre>
8169 <p>8326 <p>
8170 Rounds the given floating point number to an integer, away from zero. Uses a dedicated hardware instruction8327 Rounds the given floating point number to an integer, away from zero. Uses a dedicated hardware instruction
8171 when available.8328 when available.
...@@ -8186,7 +8343,7 @@ test "vector @splat" {...@@ -8186,7 +8343,7 @@ test "vector @splat" {
8186 {#header_close#}8343 {#header_close#}
81878344
8188 {#header_open|@tagName#}8345 {#header_open|@tagName#}
8189 <pre>{#syntax#}@tagName(value: var) []const u8{#endsyntax#}</pre>8346 <pre>{#syntax#}@tagName(value: anytype) []const u8{#endsyntax#}</pre>
8190 <p>8347 <p>
8191 Converts an enum value or union value to a slice of bytes representing the name.</p><p>If the enum is non-exhaustive and the tag value does not map to a name, it invokes safety-checked {#link|Undefined Behavior#}.8348 Converts an enum value or union value to a slice of bytes representing the name.</p><p>If the enum is non-exhaustive and the tag value does not map to a name, it invokes safety-checked {#link|Undefined Behavior#}.
8192 </p>8349 </p>
...@@ -8205,7 +8362,7 @@ test "vector @splat" {...@@ -8205,7 +8362,7 @@ test "vector @splat" {
8205 {#header_open|@This#}8362 {#header_open|@This#}
8206 <pre>{#syntax#}@This() type{#endsyntax#}</pre>8363 <pre>{#syntax#}@This() type{#endsyntax#}</pre>
8207 <p>8364 <p>
8208 Returns the innermost struct or union that this function call is inside.8365 Returns the innermost struct, enum, or union that this function call is inside.
8209 This can be useful for an anonymous struct that needs to refer to itself:8366 This can be useful for an anonymous struct that needs to refer to itself:
8210 </p>8367 </p>
8211 {#code_begin|test#}8368 {#code_begin|test#}
...@@ -8237,7 +8394,7 @@ fn List(comptime T: type) type {...@@ -8237,7 +8394,7 @@ fn List(comptime T: type) type {
8237 {#header_close#}8394 {#header_close#}
82388395
8239 {#header_open|@truncate#}8396 {#header_open|@truncate#}
8240 <pre>{#syntax#}@truncate(comptime T: type, integer: var) T{#endsyntax#}</pre>8397 <pre>{#syntax#}@truncate(comptime T: type, integer: anytype) T{#endsyntax#}</pre>
8241 <p>8398 <p>
8242 This function truncates bits from an integer type, resulting in a smaller8399 This function truncates bits from an integer type, resulting in a smaller
8243 or same-sized integer type.8400 or same-sized integer type.
...@@ -8380,6 +8537,7 @@ fn foo(comptime T: type, ptr: *T) T {...@@ -8380,6 +8537,7 @@ fn foo(comptime T: type, ptr: *T) T {
8380 {#header_close#}8537 {#header_close#}
83818538
8382 {#header_open|Opaque Types#}8539 {#header_open|Opaque Types#}
8540 <p>
8383 {#syntax#}@Type(.Opaque){#endsyntax#} creates a new type with an unknown (but non-zero) size and alignment.8541 {#syntax#}@Type(.Opaque){#endsyntax#} creates a new type with an unknown (but non-zero) size and alignment.
8384 </p>8542 </p>
8385 <p>8543 <p>
...@@ -8555,7 +8713,7 @@ const std = @import("std");...@@ -8555,7 +8713,7 @@ const std = @import("std");
8555pub fn main() void {8713pub fn main() void {
8556 var value: i32 = -1;8714 var value: i32 = -1;
8557 var unsigned = @intCast(u32, value);8715 var unsigned = @intCast(u32, value);
8558 std.debug.warn("value: {}\n", .{unsigned});8716 std.debug.print("value: {}\n", .{unsigned});
8559}8717}
8560 {#code_end#}8718 {#code_end#}
8561 <p>8719 <p>
...@@ -8577,7 +8735,7 @@ const std = @import("std");...@@ -8577,7 +8735,7 @@ const std = @import("std");
8577pub fn main() void {8735pub fn main() void {
8578 var spartan_count: u16 = 300;8736 var spartan_count: u16 = 300;
8579 const byte = @intCast(u8, spartan_count);8737 const byte = @intCast(u8, spartan_count);
8580 std.debug.warn("value: {}\n", .{byte});8738 std.debug.print("value: {}\n", .{byte});
8581}8739}
8582 {#code_end#}8740 {#code_end#}
8583 <p>8741 <p>
...@@ -8611,7 +8769,7 @@ const std = @import("std");...@@ -8611,7 +8769,7 @@ const std = @import("std");
8611pub fn main() void {8769pub fn main() void {
8612 var byte: u8 = 255;8770 var byte: u8 = 255;
8613 byte += 1;8771 byte += 1;
8614 std.debug.warn("value: {}\n", .{byte});8772 std.debug.print("value: {}\n", .{byte});
8615}8773}
8616 {#code_end#}8774 {#code_end#}
8617 {#header_close#}8775 {#header_close#}
...@@ -8629,16 +8787,16 @@ pub fn main() void {...@@ -8629,16 +8787,16 @@ pub fn main() void {
8629 <p>Example of catching an overflow for addition:</p>8787 <p>Example of catching an overflow for addition:</p>
8630 {#code_begin|exe_err#}8788 {#code_begin|exe_err#}
8631const math = @import("std").math;8789const math = @import("std").math;
8632const warn = @import("std").debug.warn;8790const print = @import("std").debug.print;
8633pub fn main() !void {8791pub fn main() !void {
8634 var byte: u8 = 255;8792 var byte: u8 = 255;
86358793
8636 byte = if (math.add(u8, byte, 1)) |result| result else |err| {8794 byte = if (math.add(u8, byte, 1)) |result| result else |err| {
8637 warn("unable to add one: {}\n", .{@errorName(err)});8795 print("unable to add one: {}\n", .{@errorName(err)});
8638 return err;8796 return err;
8639 };8797 };
86408798
8641 warn("result: {}\n", .{byte});8799 print("result: {}\n", .{byte});
8642}8800}
8643 {#code_end#}8801 {#code_end#}
8644 {#header_close#}8802 {#header_close#}
...@@ -8657,15 +8815,15 @@ pub fn main() !void {...@@ -8657,15 +8815,15 @@ pub fn main() !void {
8657 Example of {#link|@addWithOverflow#}:8815 Example of {#link|@addWithOverflow#}:
8658 </p>8816 </p>
8659 {#code_begin|exe#}8817 {#code_begin|exe#}
8660const warn = @import("std").debug.warn;8818const print = @import("std").debug.print;
8661pub fn main() void {8819pub fn main() void {
8662 var byte: u8 = 255;8820 var byte: u8 = 255;
86638821
8664 var result: u8 = undefined;8822 var result: u8 = undefined;
8665 if (@addWithOverflow(u8, byte, 10, &result)) {8823 if (@addWithOverflow(u8, byte, 10, &result)) {
8666 warn("overflowed result: {}\n", .{result});8824 print("overflowed result: {}\n", .{result});
8667 } else {8825 } else {
8668 warn("result: {}\n", .{result});8826 print("result: {}\n", .{result});
8669 }8827 }
8670}8828}
8671 {#code_end#}8829 {#code_end#}
...@@ -8710,7 +8868,7 @@ const std = @import("std");...@@ -8710,7 +8868,7 @@ const std = @import("std");
8710pub fn main() void {8868pub fn main() void {
8711 var x: u8 = 0b01010101;8869 var x: u8 = 0b01010101;
8712 var y = @shlExact(x, 2);8870 var y = @shlExact(x, 2);
8713 std.debug.warn("value: {}\n", .{y});8871 std.debug.print("value: {}\n", .{y});
8714}8872}
8715 {#code_end#}8873 {#code_end#}
8716 {#header_close#}8874 {#header_close#}
...@@ -8728,7 +8886,7 @@ const std = @import("std");...@@ -8728,7 +8886,7 @@ const std = @import("std");
8728pub fn main() void {8886pub fn main() void {
8729 var x: u8 = 0b10101010;8887 var x: u8 = 0b10101010;
8730 var y = @shrExact(x, 2);8888 var y = @shrExact(x, 2);
8731 std.debug.warn("value: {}\n", .{y});8889 std.debug.print("value: {}\n", .{y});
8732}8890}
8733 {#code_end#}8891 {#code_end#}
8734 {#header_close#}8892 {#header_close#}
...@@ -8749,7 +8907,7 @@ pub fn main() void {...@@ -8749,7 +8907,7 @@ pub fn main() void {
8749 var a: u32 = 1;8907 var a: u32 = 1;
8750 var b: u32 = 0;8908 var b: u32 = 0;
8751 var c = a / b;8909 var c = a / b;
8752 std.debug.warn("value: {}\n", .{c});8910 std.debug.print("value: {}\n", .{c});
8753}8911}
8754 {#code_end#}8912 {#code_end#}
8755 {#header_close#}8913 {#header_close#}
...@@ -8770,7 +8928,7 @@ pub fn main() void {...@@ -8770,7 +8928,7 @@ pub fn main() void {
8770 var a: u32 = 10;8928 var a: u32 = 10;
8771 var b: u32 = 0;8929 var b: u32 = 0;
8772 var c = a % b;8930 var c = a % b;
8773 std.debug.warn("value: {}\n", .{c});8931 std.debug.print("value: {}\n", .{c});
8774}8932}
8775 {#code_end#}8933 {#code_end#}
8776 {#header_close#}8934 {#header_close#}
...@@ -8791,7 +8949,7 @@ pub fn main() void {...@@ -8791,7 +8949,7 @@ pub fn main() void {
8791 var a: u32 = 10;8949 var a: u32 = 10;
8792 var b: u32 = 3;8950 var b: u32 = 3;
8793 var c = @divExact(a, b);8951 var c = @divExact(a, b);
8794 std.debug.warn("value: {}\n", .{c});8952 std.debug.print("value: {}\n", .{c});
8795}8953}
8796 {#code_end#}8954 {#code_end#}
8797 {#header_close#}8955 {#header_close#}
...@@ -8810,20 +8968,20 @@ const std = @import("std");...@@ -8810,20 +8968,20 @@ const std = @import("std");
8810pub fn main() void {8968pub fn main() void {
8811 var optional_number: ?i32 = null;8969 var optional_number: ?i32 = null;
8812 var number = optional_number.?;8970 var number = optional_number.?;
8813 std.debug.warn("value: {}\n", .{number});8971 std.debug.print("value: {}\n", .{number});
8814}8972}
8815 {#code_end#}8973 {#code_end#}
8816 <p>One way to avoid this crash is to test for null instead of assuming non-null, with8974 <p>One way to avoid this crash is to test for null instead of assuming non-null, with
8817 the {#syntax#}if{#endsyntax#} expression:</p>8975 the {#syntax#}if{#endsyntax#} expression:</p>
8818 {#code_begin|exe|test#}8976 {#code_begin|exe|test#}
8819const warn = @import("std").debug.warn;8977const print = @import("std").debug.print;
8820pub fn main() void {8978pub fn main() void {
8821 const optional_number: ?i32 = null;8979 const optional_number: ?i32 = null;
88228980
8823 if (optional_number) |number| {8981 if (optional_number) |number| {
8824 warn("got number: {}\n", .{number});8982 print("got number: {}\n", .{number});
8825 } else {8983 } else {
8826 warn("it's null\n", .{});8984 print("it's null\n", .{});
8827 }8985 }
8828}8986}
8829 {#code_end#}8987 {#code_end#}
...@@ -8846,7 +9004,7 @@ const std = @import("std");...@@ -8846,7 +9004,7 @@ const std = @import("std");
88469004
8847pub fn main() void {9005pub fn main() void {
8848 const number = getNumberOrFail() catch unreachable;9006 const number = getNumberOrFail() catch unreachable;
8849 std.debug.warn("value: {}\n", .{number});9007 std.debug.print("value: {}\n", .{number});
8850}9008}
88519009
8852fn getNumberOrFail() !i32 {9010fn getNumberOrFail() !i32 {
...@@ -8856,15 +9014,15 @@ fn getNumberOrFail() !i32 {...@@ -8856,15 +9014,15 @@ fn getNumberOrFail() !i32 {
8856 <p>One way to avoid this crash is to test for an error instead of assuming a successful result, with9014 <p>One way to avoid this crash is to test for an error instead of assuming a successful result, with
8857 the {#syntax#}if{#endsyntax#} expression:</p>9015 the {#syntax#}if{#endsyntax#} expression:</p>
8858 {#code_begin|exe#}9016 {#code_begin|exe#}
8859const warn = @import("std").debug.warn;9017const print = @import("std").debug.print;
88609018
8861pub fn main() void {9019pub fn main() void {
8862 const result = getNumberOrFail();9020 const result = getNumberOrFail();
88639021
8864 if (result) |number| {9022 if (result) |number| {
8865 warn("got number: {}\n", .{number});9023 print("got number: {}\n", .{number});
8866 } else |err| {9024 } else |err| {
8867 warn("got error: {}\n", .{@errorName(err)});9025 print("got error: {}\n", .{@errorName(err)});
8868 }9026 }
8869}9027}
88709028
...@@ -8891,7 +9049,7 @@ pub fn main() void {...@@ -8891,7 +9049,7 @@ pub fn main() void {
8891 var err = error.AnError;9049 var err = error.AnError;
8892 var number = @errorToInt(err) + 500;9050 var number = @errorToInt(err) + 500;
8893 var invalid_err = @intToError(number);9051 var invalid_err = @intToError(number);
8894 std.debug.warn("value: {}\n", .{number});9052 std.debug.print("value: {}\n", .{number});
8895}9053}
8896 {#code_end#}9054 {#code_end#}
8897 {#header_close#}9055 {#header_close#}
...@@ -8921,7 +9079,7 @@ const Foo = enum {...@@ -8921,7 +9079,7 @@ const Foo = enum {
8921pub fn main() void {9079pub fn main() void {
8922 var a: u2 = 3;9080 var a: u2 = 3;
8923 var b = @intToEnum(Foo, a);9081 var b = @intToEnum(Foo, a);
8924 std.debug.warn("value: {}\n", .{@tagName(b)});9082 std.debug.print("value: {}\n", .{@tagName(b)});
8925}9083}
8926 {#code_end#}9084 {#code_end#}
8927 {#header_close#}9085 {#header_close#}
...@@ -8958,7 +9116,7 @@ pub fn main() void {...@@ -8958,7 +9116,7 @@ pub fn main() void {
8958}9116}
8959fn foo(set1: Set1) void {9117fn foo(set1: Set1) void {
8960 const x = @errSetCast(Set2, set1);9118 const x = @errSetCast(Set2, set1);
8961 std.debug.warn("value: {}\n", .{x});9119 std.debug.print("value: {}\n", .{x});
8962}9120}
8963 {#code_end#}9121 {#code_end#}
8964 {#header_close#}9122 {#header_close#}
...@@ -9015,7 +9173,7 @@ pub fn main() void {...@@ -9015,7 +9173,7 @@ pub fn main() void {
90159173
9016fn bar(f: *Foo) void {9174fn bar(f: *Foo) void {
9017 f.float = 12.34;9175 f.float = 12.34;
9018 std.debug.warn("value: {}\n", .{f.float});9176 std.debug.print("value: {}\n", .{f.float});
9019}9177}
9020 {#code_end#}9178 {#code_end#}
9021 <p>9179 <p>
...@@ -9039,7 +9197,7 @@ pub fn main() void {...@@ -9039,7 +9197,7 @@ pub fn main() void {
90399197
9040fn bar(f: *Foo) void {9198fn bar(f: *Foo) void {
9041 f.* = Foo{ .float = 12.34 };9199 f.* = Foo{ .float = 12.34 };
9042 std.debug.warn("value: {}\n", .{f.float});9200 std.debug.print("value: {}\n", .{f.float});
9043}9201}
9044 {#code_end#}9202 {#code_end#}
9045 <p>9203 <p>
...@@ -9058,7 +9216,7 @@ pub fn main() void {...@@ -9058,7 +9216,7 @@ pub fn main() void {
9058 var f = Foo{ .int = 42 };9216 var f = Foo{ .int = 42 };
9059 f = Foo{ .float = undefined };9217 f = Foo{ .float = undefined };
9060 bar(&f);9218 bar(&f);
9061 std.debug.warn("value: {}\n", .{f.float});9219 std.debug.print("value: {}\n", .{f.float});
9062}9220}
90639221
9064fn bar(f: *Foo) void {9222fn bar(f: *Foo) void {
...@@ -9178,7 +9336,7 @@ pub fn main() !void {...@@ -9178,7 +9336,7 @@ pub fn main() !void {
9178 const allocator = &arena.allocator;9336 const allocator = &arena.allocator;
91799337
9180 const ptr = try allocator.create(i32);9338 const ptr = try allocator.create(i32);
9181 std.debug.warn("ptr={*}\n", .{ptr});9339 std.debug.print("ptr={*}\n", .{ptr});
9182}9340}
9183 {#code_end#}9341 {#code_end#}
9184 When using this kind of allocator, there is no need to free anything manually. Everything9342 When using this kind of allocator, there is no need to free anything manually. Everything
...@@ -9712,7 +9870,7 @@ pub fn main() !void {...@@ -9712,7 +9870,7 @@ pub fn main() !void {
9712 defer std.process.argsFree(std.heap.page_allocator, args);9870 defer std.process.argsFree(std.heap.page_allocator, args);
97139871
9714 for (args) |arg, i| {9872 for (args) |arg, i| {
9715 std.debug.warn("{}: {}\n", .{i, arg});9873 std.debug.print("{}: {}\n", .{i, arg});
9716 }9874 }
9717}9875}
9718 {#code_end#}9876 {#code_end#}
...@@ -9734,12 +9892,12 @@ pub fn main() !void {...@@ -9734,12 +9892,12 @@ pub fn main() !void {
9734 try preopens.populate();9892 try preopens.populate();
97359893
9736 for (preopens.asSlice()) |preopen, i| {9894 for (preopens.asSlice()) |preopen, i| {
9737 std.debug.warn("{}: {}\n", .{ i, preopen });9895 std.debug.print("{}: {}\n", .{ i, preopen });
9738 }9896 }
9739}9897}
9740 {#code_end#}9898 {#code_end#}
9741 <pre><code>$ wasmtime --dir=. preopens.wasm9899 <pre><code>$ wasmtime --dir=. preopens.wasm
97420: { .fd = 3, .Dir = '.' }99000: Preopen{ .fd = 3, .type = PreopenType{ .Dir = '.' } }
9743</code></pre>9901</code></pre>
9744 {#header_close#}9902 {#header_close#}
9745 {#header_close#}9903 {#header_close#}
...@@ -10158,7 +10316,7 @@ TopLevelDecl...@@ -10158,7 +10316,7 @@ TopLevelDecl
10158 / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl10316 / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
10159 / KEYWORD_usingnamespace Expr SEMICOLON10317 / KEYWORD_usingnamespace Expr SEMICOLON
1016010318
10161FnProto &lt;- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)10319FnProto &lt;- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype / TypeExpr)
1016210320
10163VarDecl &lt;- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON10321VarDecl &lt;- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
1016410322
...@@ -10330,7 +10488,7 @@ LinkSection &lt;- KEYWORD_linksection LPAREN Expr RPAREN...@@ -10330,7 +10488,7 @@ LinkSection &lt;- KEYWORD_linksection LPAREN Expr RPAREN
10330ParamDecl &lt;- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType10488ParamDecl &lt;- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
1033110489
10332ParamType10490ParamType
10333 &lt;- KEYWORD_var10491 &lt;- KEYWORD_anytype
10334 / DOT310492 / DOT3
10335 / TypeExpr10493 / TypeExpr
1033610494
...@@ -10568,6 +10726,7 @@ KEYWORD_align &lt;- 'align' end_of_word...@@ -10568,6 +10726,7 @@ KEYWORD_align &lt;- 'align' end_of_word
10568KEYWORD_allowzero &lt;- 'allowzero' end_of_word10726KEYWORD_allowzero &lt;- 'allowzero' end_of_word
10569KEYWORD_and &lt;- 'and' end_of_word10727KEYWORD_and &lt;- 'and' end_of_word
10570KEYWORD_anyframe &lt;- 'anyframe' end_of_word10728KEYWORD_anyframe &lt;- 'anyframe' end_of_word
10729KEYWORD_anytype &lt;- 'anytype' end_of_word
10571KEYWORD_asm &lt;- 'asm' end_of_word10730KEYWORD_asm &lt;- 'asm' end_of_word
10572KEYWORD_async &lt;- 'async' end_of_word10731KEYWORD_async &lt;- 'async' end_of_word
10573KEYWORD_await &lt;- 'await' end_of_word10732KEYWORD_await &lt;- 'await' end_of_word
...@@ -10613,14 +10772,14 @@ KEYWORD_var &lt;- 'var' end_of_word...@@ -10613,14 +10772,14 @@ KEYWORD_var &lt;- 'var' end_of_word
10613KEYWORD_volatile &lt;- 'volatile' end_of_word10772KEYWORD_volatile &lt;- 'volatile' end_of_word
10614KEYWORD_while &lt;- 'while' end_of_word10773KEYWORD_while &lt;- 'while' end_of_word
1061510774
10616keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_allowzero / KEYWORD_asm10775keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_anyframe / KEYWORD_anytype
10617 / KEYWORD_async / KEYWORD_await / KEYWORD_break10776 / KEYWORD_allowzero / KEYWORD_asm / KEYWORD_async / KEYWORD_await / KEYWORD_break
10618 / KEYWORD_catch / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue10777 / KEYWORD_catch / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue
10619 / KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer10778 / KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer
10620 / KEYWORD_error / KEYWORD_export / KEYWORD_extern / KEYWORD_false10779 / KEYWORD_error / KEYWORD_export / KEYWORD_extern / KEYWORD_false
10621 / KEYWORD_fn / KEYWORD_for / KEYWORD_if / KEYWORD_inline10780 / KEYWORD_fn / KEYWORD_for / KEYWORD_if / KEYWORD_inline
10622 / KEYWORD_noalias / KEYWORD_null / KEYWORD_or10781 / KEYWORD_noalias / KEYWORD_null / KEYWORD_or
10623 / KEYWORD_orelse / KEYWORD_packed / KEYWORD_anyframe / KEYWORD_pub10782 / KEYWORD_orelse / KEYWORD_packed / KEYWORD_pub
10624 / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection10783 / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection
10625 / KEYWORD_struct / KEYWORD_suspend10784 / KEYWORD_struct / KEYWORD_suspend
10626 / KEYWORD_switch / KEYWORD_test / KEYWORD_threadlocal / KEYWORD_true / KEYWORD_try10785 / KEYWORD_switch / KEYWORD_test / KEYWORD_threadlocal / KEYWORD_true / KEYWORD_try
lib/std/array_list.zig+108-15
...@@ -53,7 +53,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -53,7 +53,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
53 /// Deprecated: use `items` field directly.53 /// Deprecated: use `items` field directly.
54 /// Return contents as a slice. Only valid while the list54 /// Return contents as a slice. Only valid while the list
55 /// doesn't change size.55 /// doesn't change size.
56 pub fn span(self: var) @TypeOf(self.items) {56 pub fn span(self: anytype) @TypeOf(self.items) {
57 return self.items;57 return self.items;
58 }58 }
5959
...@@ -162,19 +162,24 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -162,19 +162,24 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
162 mem.copy(T, self.items[oldlen..], items);162 mem.copy(T, self.items[oldlen..], items);
163 }163 }
164164
165 /// Same as `append` except it returns the number of bytes written, which is always the same165 pub usingnamespace if (T != u8) struct {} else struct {
166 /// as `m.len`. The purpose of this function existing is to match `std.io.OutStream` API.166 pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
167 /// This function may be called only when `T` is `u8`.
168 fn appendWrite(self: *Self, m: []const u8) !usize {
169 try self.appendSlice(m);
170 return m.len;
171 }
172167
173 /// Initializes an OutStream which will append to the list.168 /// Initializes a Writer which will append to the list.
174 /// This function may be called only when `T` is `u8`.169 pub fn writer(self: *Self) Writer {
175 pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) {170 return .{ .context = self };
176 return .{ .context = self };171 }
177 }172
173 /// Deprecated: use `writer`
174 pub const outStream = writer;
175
176 /// Same as `append` except it returns the number of bytes written, which is always the same
177 /// as `m.len`. The purpose of this function existing is to match `std.io.Writer` API.
178 fn appendWrite(self: *Self, m: []const u8) !usize {
179 try self.appendSlice(m);
180 return m.len;
181 }
182 };
178183
179 /// Append a value to the list `n` times.184 /// Append a value to the list `n` times.
180 /// Allocates more memory as necessary.185 /// Allocates more memory as necessary.
...@@ -205,6 +210,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -205,6 +210,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
205 self.capacity = new_len;210 self.capacity = new_len;
206 }211 }
207212
213 /// Reduce length to `new_len`.
214 /// Invalidates element pointers.
215 /// Keeps capacity the same.
216 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
217 assert(new_len <= self.items.len);
218 self.items.len = new_len;
219 }
220
208 pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {221 pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {
209 var better_capacity = self.capacity;222 var better_capacity = self.capacity;
210 if (better_capacity >= new_capacity) return;223 if (better_capacity >= new_capacity) return;
...@@ -214,7 +227,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -214,7 +227,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
214 if (better_capacity >= new_capacity) break;227 if (better_capacity >= new_capacity) break;
215 }228 }
216229
217 const new_memory = try self.allocator.realloc(self.allocatedSlice(), better_capacity);230 const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);
218 self.items.ptr = new_memory.ptr;231 self.items.ptr = new_memory.ptr;
219 self.capacity = new_memory.len;232 self.capacity = new_memory.len;
220 }233 }
...@@ -244,6 +257,24 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -244,6 +257,24 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
244 return &self.items[self.items.len - 1];257 return &self.items[self.items.len - 1];
245 }258 }
246259
260 /// Resize the array, adding `n` new elements, which have `undefined` values.
261 /// The return value is an array pointing to the newly allocated elements.
262 pub fn addManyAsArray(self: *Self, comptime n: usize) !*[n]T {
263 const prev_len = self.items.len;
264 try self.resize(self.items.len + n);
265 return self.items[prev_len..][0..n];
266 }
267
268 /// Resize the array, adding `n` new elements, which have `undefined` values.
269 /// The return value is an array pointing to the newly allocated elements.
270 /// Asserts that there is already space for the new item without allocating more.
271 pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
272 assert(self.items.len + n <= self.capacity);
273 const prev_len = self.items.len;
274 self.items.len += n;
275 return self.items[prev_len..][0..n];
276 }
277
247 /// Remove and return the last element from the list.278 /// Remove and return the last element from the list.
248 /// Asserts the list has at least one item.279 /// Asserts the list has at least one item.
249 pub fn pop(self: *Self) T {280 pub fn pop(self: *Self) T {
...@@ -427,6 +458,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -427,6 +458,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
427 self.capacity = new_len;458 self.capacity = new_len;
428 }459 }
429460
461 /// Reduce length to `new_len`.
462 /// Invalidates element pointers.
463 /// Keeps capacity the same.
464 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
465 assert(new_len <= self.items.len);
466 self.items.len = new_len;
467 }
468
430 pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void {469 pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void {
431 var better_capacity = self.capacity;470 var better_capacity = self.capacity;
432 if (better_capacity >= new_capacity) return;471 if (better_capacity >= new_capacity) return;
...@@ -436,7 +475,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -436,7 +475,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
436 if (better_capacity >= new_capacity) break;475 if (better_capacity >= new_capacity) break;
437 }476 }
438477
439 const new_memory = try allocator.realloc(self.allocatedSlice(), better_capacity);478 const new_memory = try allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);
440 self.items.ptr = new_memory.ptr;479 self.items.ptr = new_memory.ptr;
441 self.capacity = new_memory.len;480 self.capacity = new_memory.len;
442 }481 }
...@@ -467,6 +506,24 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -467,6 +506,24 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
467 return &self.items[self.items.len - 1];506 return &self.items[self.items.len - 1];
468 }507 }
469508
509 /// Resize the array, adding `n` new elements, which have `undefined` values.
510 /// The return value is an array pointing to the newly allocated elements.
511 pub fn addManyAsArray(self: *Self, allocator: *Allocator, comptime n: usize) !*[n]T {
512 const prev_len = self.items.len;
513 try self.resize(allocator, self.items.len + n);
514 return self.items[prev_len..][0..n];
515 }
516
517 /// Resize the array, adding `n` new elements, which have `undefined` values.
518 /// The return value is an array pointing to the newly allocated elements.
519 /// Asserts that there is already space for the new item without allocating more.
520 pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
521 assert(self.items.len + n <= self.capacity);
522 const prev_len = self.items.len;
523 self.items.len += n;
524 return self.items[prev_len..][0..n];
525 }
526
470 /// Remove and return the last element from the list.527 /// Remove and return the last element from the list.
471 /// Asserts the list has at least one item.528 /// Asserts the list has at least one item.
472 /// This operation does not invalidate any element pointers.529 /// This operation does not invalidate any element pointers.
...@@ -694,3 +751,39 @@ test "std.ArrayList.shrink still sets length on error.OutOfMemory" {...@@ -694,3 +751,39 @@ test "std.ArrayList.shrink still sets length on error.OutOfMemory" {
694 list.shrink(1);751 list.shrink(1);
695 testing.expect(list.items.len == 1);752 testing.expect(list.items.len == 1);
696}753}
754
755test "std.ArrayList.writer" {
756 var list = ArrayList(u8).init(std.testing.allocator);
757 defer list.deinit();
758
759 const writer = list.writer();
760 try writer.writeAll("a");
761 try writer.writeAll("bc");
762 try writer.writeAll("d");
763 try writer.writeAll("efg");
764 testing.expectEqualSlices(u8, list.items, "abcdefg");
765}
766
767test "addManyAsArray" {
768 const a = std.testing.allocator;
769 {
770 var list = ArrayList(u8).init(a);
771 defer list.deinit();
772
773 (try list.addManyAsArray(4)).* = "aoeu".*;
774 try list.ensureCapacity(8);
775 list.addManyAsArrayAssumeCapacity(4).* = "asdf".*;
776
777 testing.expectEqualSlices(u8, list.items, "aoeuasdf");
778 }
779 {
780 var list = ArrayListUnmanaged(u8){};
781 defer list.deinit(a);
782
783 (try list.addManyAsArray(a, 4)).* = "aoeu".*;
784 try list.ensureCapacity(a, 8);
785 list.addManyAsArrayAssumeCapacity(4).* = "asdf".*;
786
787 testing.expectEqualSlices(u8, list.items, "aoeuasdf");
788 }
789}
lib/std/array_list_sentineled.zig+2-2
...@@ -69,7 +69,7 @@ pub fn ArrayListSentineled(comptime T: type, comptime sentinel: T) type {...@@ -69,7 +69,7 @@ pub fn ArrayListSentineled(comptime T: type, comptime sentinel: T) type {
69 }69 }
7070
71 /// Only works when `T` is `u8`.71 /// Only works when `T` is `u8`.
72 pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: var) !Self {72 pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: anytype) !Self {
73 const size = std.math.cast(usize, std.fmt.count(format, args)) catch |err| switch (err) {73 const size = std.math.cast(usize, std.fmt.count(format, args)) catch |err| switch (err) {
74 error.Overflow => return error.OutOfMemory,74 error.Overflow => return error.OutOfMemory,
75 };75 };
...@@ -82,7 +82,7 @@ pub fn ArrayListSentineled(comptime T: type, comptime sentinel: T) type {...@@ -82,7 +82,7 @@ pub fn ArrayListSentineled(comptime T: type, comptime sentinel: T) type {
82 self.list.deinit();82 self.list.deinit();
83 }83 }
8484
85 pub fn span(self: var) @TypeOf(self.list.items[0..:sentinel]) {85 pub fn span(self: anytype) @TypeOf(self.list.items[0..:sentinel]) {
86 return self.list.items[0..self.len() :sentinel];86 return self.list.items[0..self.len() :sentinel];
87 }87 }
8888
lib/std/atomic/queue.zig+2-2
...@@ -123,10 +123,10 @@ pub fn Queue(comptime T: type) type {...@@ -123,10 +123,10 @@ pub fn Queue(comptime T: type) type {
123 /// Dumps the contents of the queue to `stream`.123 /// Dumps the contents of the queue to `stream`.
124 /// Up to 4 elements from the head are dumped and the tail of the queue is124 /// Up to 4 elements from the head are dumped and the tail of the queue is
125 /// dumped as well.125 /// dumped as well.
126 pub fn dumpToStream(self: *Self, stream: var) !void {126 pub fn dumpToStream(self: *Self, stream: anytype) !void {
127 const S = struct {127 const S = struct {
128 fn dumpRecursive(128 fn dumpRecursive(
129 s: var,129 s: anytype,
130 optional_node: ?*Node,130 optional_node: ?*Node,
131 indent: usize,131 indent: usize,
132 comptime depth: comptime_int,132 comptime depth: comptime_int,
lib/std/buf_map.zig+8-9
...@@ -33,10 +33,10 @@ pub const BufMap = struct {...@@ -33,10 +33,10 @@ pub const BufMap = struct {
33 pub fn setMove(self: *BufMap, key: []u8, value: []u8) !void {33 pub fn setMove(self: *BufMap, key: []u8, value: []u8) !void {
34 const get_or_put = try self.hash_map.getOrPut(key);34 const get_or_put = try self.hash_map.getOrPut(key);
35 if (get_or_put.found_existing) {35 if (get_or_put.found_existing) {
36 self.free(get_or_put.kv.key);36 self.free(get_or_put.entry.key);
37 get_or_put.kv.key = key;37 get_or_put.entry.key = key;
38 }38 }
39 get_or_put.kv.value = value;39 get_or_put.entry.value = value;
40 }40 }
4141
42 /// `key` and `value` are copied into the BufMap.42 /// `key` and `value` are copied into the BufMap.
...@@ -45,19 +45,18 @@ pub const BufMap = struct {...@@ -45,19 +45,18 @@ pub const BufMap = struct {
45 errdefer self.free(value_copy);45 errdefer self.free(value_copy);
46 const get_or_put = try self.hash_map.getOrPut(key);46 const get_or_put = try self.hash_map.getOrPut(key);
47 if (get_or_put.found_existing) {47 if (get_or_put.found_existing) {
48 self.free(get_or_put.kv.value);48 self.free(get_or_put.entry.value);
49 } else {49 } else {
50 get_or_put.kv.key = self.copy(key) catch |err| {50 get_or_put.entry.key = self.copy(key) catch |err| {
51 _ = self.hash_map.remove(key);51 _ = self.hash_map.remove(key);
52 return err;52 return err;
53 };53 };
54 }54 }
55 get_or_put.kv.value = value_copy;55 get_or_put.entry.value = value_copy;
56 }56 }
5757
58 pub fn get(self: BufMap, key: []const u8) ?[]const u8 {58 pub fn get(self: BufMap, key: []const u8) ?[]const u8 {
59 const entry = self.hash_map.get(key) orelse return null;59 return self.hash_map.get(key);
60 return entry.value;
61 }60 }
6261
63 pub fn delete(self: *BufMap, key: []const u8) void {62 pub fn delete(self: *BufMap, key: []const u8) void {
...@@ -79,7 +78,7 @@ pub const BufMap = struct {...@@ -79,7 +78,7 @@ pub const BufMap = struct {
79 }78 }
8079
81 fn copy(self: BufMap, value: []const u8) ![]u8 {80 fn copy(self: BufMap, value: []const u8) ![]u8 {
82 return mem.dupe(self.hash_map.allocator, u8, value);81 return self.hash_map.allocator.dupe(u8, value);
83 }82 }
84};83};
8584
lib/std/buf_set.zig+3-5
...@@ -14,14 +14,12 @@ pub const BufSet = struct {...@@ -14,14 +14,12 @@ pub const BufSet = struct {
14 return self;14 return self;
15 }15 }
1616
17 pub fn deinit(self: *const BufSet) void {17 pub fn deinit(self: *BufSet) void {
18 var it = self.hash_map.iterator();18 for (self.hash_map.items()) |entry| {
19 while (true) {
20 const entry = it.next() orelse break;
21 self.free(entry.key);19 self.free(entry.key);
22 }20 }
23
24 self.hash_map.deinit();21 self.hash_map.deinit();
22 self.* = undefined;
25 }23 }
2624
27 pub fn put(self: *BufSet, key: []const u8) !void {25 pub fn put(self: *BufSet, key: []const u8) !void {
lib/std/build.zig+24-16
...@@ -286,7 +286,7 @@ pub const Builder = struct {...@@ -286,7 +286,7 @@ pub const Builder = struct {
286 }286 }
287287
288 pub fn dupe(self: *Builder, bytes: []const u8) []u8 {288 pub fn dupe(self: *Builder, bytes: []const u8) []u8 {
289 return mem.dupe(self.allocator, u8, bytes) catch unreachable;289 return self.allocator.dupe(u8, bytes) catch unreachable;
290 }290 }
291291
292 pub fn dupePath(self: *Builder, bytes: []const u8) []u8 {292 pub fn dupePath(self: *Builder, bytes: []const u8) []u8 {
...@@ -312,7 +312,7 @@ pub const Builder = struct {...@@ -312,7 +312,7 @@ pub const Builder = struct {
312 return write_file_step;312 return write_file_step;
313 }313 }
314314
315 pub fn addLog(self: *Builder, comptime format: []const u8, args: var) *LogStep {315 pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep {
316 const data = self.fmt(format, args);316 const data = self.fmt(format, args);
317 const log_step = self.allocator.create(LogStep) catch unreachable;317 const log_step = self.allocator.create(LogStep) catch unreachable;
318 log_step.* = LogStep.init(self, data);318 log_step.* = LogStep.init(self, data);
...@@ -422,12 +422,12 @@ pub const Builder = struct {...@@ -422,12 +422,12 @@ pub const Builder = struct {
422 .type_id = type_id,422 .type_id = type_id,
423 .description = description,423 .description = description,
424 };424 };
425 if ((self.available_options_map.put(name, available_option) catch unreachable) != null) {425 if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
426 panic("Option '{}' declared twice", .{name});426 panic("Option '{}' declared twice", .{name});
427 }427 }
428 self.available_options_list.append(available_option) catch unreachable;428 self.available_options_list.append(available_option) catch unreachable;
429429
430 const entry = self.user_input_options.get(name) orelse return null;430 const entry = self.user_input_options.getEntry(name) orelse return null;
431 entry.value.used = true;431 entry.value.used = true;
432 switch (type_id) {432 switch (type_id) {
433 TypeId.Bool => switch (entry.value.value) {433 TypeId.Bool => switch (entry.value.value) {
...@@ -512,7 +512,7 @@ pub const Builder = struct {...@@ -512,7 +512,7 @@ pub const Builder = struct {
512 if (self.release_mode != null) {512 if (self.release_mode != null) {
513 @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice");513 @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice");
514 }514 }
515 const description = self.fmt("create a release build ({})", .{@tagName(mode)});515 const description = self.fmt("Create a release build ({})", .{@tagName(mode)});
516 self.is_release = self.option(bool, "release", description) orelse false;516 self.is_release = self.option(bool, "release", description) orelse false;
517 self.release_mode = if (self.is_release) mode else builtin.Mode.Debug;517 self.release_mode = if (self.is_release) mode else builtin.Mode.Debug;
518 }518 }
...@@ -522,9 +522,9 @@ pub const Builder = struct {...@@ -522,9 +522,9 @@ pub const Builder = struct {
522 pub fn standardReleaseOptions(self: *Builder) builtin.Mode {522 pub fn standardReleaseOptions(self: *Builder) builtin.Mode {
523 if (self.release_mode) |mode| return mode;523 if (self.release_mode) |mode| return mode;
524524
525 const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") orelse false;525 const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false;
526 const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") orelse false;526 const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false;
527 const release_small = self.option(bool, "release-small", "size optimizations on and safety off") orelse false;527 const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false;
528528
529 const mode = if (release_safe and !release_fast and !release_small)529 const mode = if (release_safe and !release_fast and !release_small)
530 builtin.Mode.ReleaseSafe530 builtin.Mode.ReleaseSafe
...@@ -555,7 +555,7 @@ pub const Builder = struct {...@@ -555,7 +555,7 @@ pub const Builder = struct {
555 const triple = self.option(555 const triple = self.option(
556 []const u8,556 []const u8,
557 "target",557 "target",
558 "The CPU architecture, OS, and ABI to build for.",558 "The CPU architecture, OS, and ABI to build for",
559 ) orelse return args.default_target;559 ) orelse return args.default_target;
560560
561 // TODO add cpu and features as part of the target triple561 // TODO add cpu and features as part of the target triple
...@@ -634,7 +634,7 @@ pub const Builder = struct {...@@ -634,7 +634,7 @@ pub const Builder = struct {
634 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {634 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
635 const gop = try self.user_input_options.getOrPut(name);635 const gop = try self.user_input_options.getOrPut(name);
636 if (!gop.found_existing) {636 if (!gop.found_existing) {
637 gop.kv.value = UserInputOption{637 gop.entry.value = UserInputOption{
638 .name = name,638 .name = name,
639 .value = UserValue{ .Scalar = value },639 .value = UserValue{ .Scalar = value },
640 .used = false,640 .used = false,
...@@ -643,7 +643,7 @@ pub const Builder = struct {...@@ -643,7 +643,7 @@ pub const Builder = struct {
643 }643 }
644644
645 // option already exists645 // option already exists
646 switch (gop.kv.value.value) {646 switch (gop.entry.value.value) {
647 UserValue.Scalar => |s| {647 UserValue.Scalar => |s| {
648 // turn it into a list648 // turn it into a list
649 var list = ArrayList([]const u8).init(self.allocator);649 var list = ArrayList([]const u8).init(self.allocator);
...@@ -675,7 +675,7 @@ pub const Builder = struct {...@@ -675,7 +675,7 @@ pub const Builder = struct {
675 pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool {675 pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool {
676 const gop = try self.user_input_options.getOrPut(name);676 const gop = try self.user_input_options.getOrPut(name);
677 if (!gop.found_existing) {677 if (!gop.found_existing) {
678 gop.kv.value = UserInputOption{678 gop.entry.value = UserInputOption{
679 .name = name,679 .name = name,
680 .value = UserValue{ .Flag = {} },680 .value = UserValue{ .Flag = {} },
681 .used = false,681 .used = false,
...@@ -684,7 +684,7 @@ pub const Builder = struct {...@@ -684,7 +684,7 @@ pub const Builder = struct {
684 }684 }
685685
686 // option already exists686 // option already exists
687 switch (gop.kv.value.value) {687 switch (gop.entry.value.value) {
688 UserValue.Scalar => |s| {688 UserValue.Scalar => |s| {
689 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", .{ name, name, s });689 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", .{ name, name, s });
690 return true;690 return true;
...@@ -883,7 +883,7 @@ pub const Builder = struct {...@@ -883,7 +883,7 @@ pub const Builder = struct {
883 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;883 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;
884 }884 }
885885
886 pub fn fmt(self: *Builder, comptime format: []const u8, args: var) []u8 {886 pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 {
887 return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;887 return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;
888 }888 }
889889
...@@ -1905,10 +1905,11 @@ pub const LibExeObjStep = struct {...@@ -1905,10 +1905,11 @@ pub const LibExeObjStep = struct {
1905 builder.allocator,1905 builder.allocator,
1906 &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) },1906 &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) },
1907 );1907 );
1908 try fs.cwd().writeFile(build_options_file, self.build_options_contents.span());1908 const path_from_root = builder.pathFromRoot(build_options_file);
1909 try fs.cwd().writeFile(path_from_root, self.build_options_contents.span());
1909 try zig_args.append("--pkg-begin");1910 try zig_args.append("--pkg-begin");
1910 try zig_args.append("build_options");1911 try zig_args.append("build_options");
1911 try zig_args.append(builder.pathFromRoot(build_options_file));1912 try zig_args.append(path_from_root);
1912 try zig_args.append("--pkg-end");1913 try zig_args.append("--pkg-end");
1913 }1914 }
19141915
...@@ -2558,3 +2559,10 @@ pub const InstalledFile = struct {...@@ -2558,3 +2559,10 @@ pub const InstalledFile = struct {
2558 dir: InstallDir,2559 dir: InstallDir,
2559 path: []const u8,2560 path: []const u8,
2560};2561};
2562
2563test "" {
2564 // The only purpose of this test is to get all these untested functions
2565 // to be referenced to avoid regression so it is okay to skip some targets.
2566 if (comptime std.Target.current.cpu.arch.ptrBitWidth() == 64)
2567 std.meta.refAllDecls(@This());
2568}
lib/std/build/emit_raw.zig+5-1
...@@ -126,7 +126,7 @@ const BinaryElfOutput = struct {...@@ -126,7 +126,7 @@ const BinaryElfOutput = struct {
126 return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize);126 return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize);
127 }127 }
128128
129 fn sectionValidForOutput(shdr: var) bool {129 fn sectionValidForOutput(shdr: anytype) bool {
130 return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and130 return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and
131 ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC);131 ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC);
132 }132 }
...@@ -215,3 +215,7 @@ pub const InstallRawStep = struct {...@@ -215,3 +215,7 @@ pub const InstallRawStep = struct {
215 try emitRaw(builder.allocator, full_src_path, full_dest_path);215 try emitRaw(builder.allocator, full_src_path, full_dest_path);
216 }216 }
217};217};
218
219test "" {
220 std.meta.refAllDecls(InstallRawStep);
221}
lib/std/builtin.zig+31-14
...@@ -131,6 +131,15 @@ pub const CallingConvention = enum {...@@ -131,6 +131,15 @@ pub const CallingConvention = enum {
131 AAPCSVFP,131 AAPCSVFP,
132};132};
133133
134/// This data structure is used by the Zig language code generation and
135/// therefore must be kept in sync with the compiler implementation.
136pub const SourceLocation = struct {
137 file: [:0]const u8,
138 fn_name: [:0]const u8,
139 line: u32,
140 column: u32,
141};
142
134pub const TypeId = @TagType(TypeInfo);143pub const TypeId = @TagType(TypeInfo);
135144
136/// This data structure is used by the Zig language code generation and145/// This data structure is used by the Zig language code generation and
...@@ -157,7 +166,7 @@ pub const TypeInfo = union(enum) {...@@ -157,7 +166,7 @@ pub const TypeInfo = union(enum) {
157 Fn: Fn,166 Fn: Fn,
158 BoundFn: Fn,167 BoundFn: Fn,
159 Opaque: void,168 Opaque: void,
160 Frame: void,169 Frame: Frame,
161 AnyFrame: AnyFrame,170 AnyFrame: AnyFrame,
162 Vector: Vector,171 Vector: Vector,
163 EnumLiteral: void,172 EnumLiteral: void,
...@@ -189,7 +198,7 @@ pub const TypeInfo = union(enum) {...@@ -189,7 +198,7 @@ pub const TypeInfo = union(enum) {
189 /// The type of the sentinel is the element type of the pointer, which is198 /// The type of the sentinel is the element type of the pointer, which is
190 /// the value of the `child` field in this struct. However there is no way199 /// the value of the `child` field in this struct. However there is no way
191 /// to refer to that type here, so we use `var`.200 /// to refer to that type here, so we use `var`.
192 sentinel: var,201 sentinel: anytype,
193202
194 /// This data structure is used by the Zig language code generation and203 /// This data structure is used by the Zig language code generation and
195 /// therefore must be kept in sync with the compiler implementation.204 /// therefore must be kept in sync with the compiler implementation.
...@@ -211,7 +220,7 @@ pub const TypeInfo = union(enum) {...@@ -211,7 +220,7 @@ pub const TypeInfo = union(enum) {
211 /// The type of the sentinel is the element type of the array, which is220 /// The type of the sentinel is the element type of the array, which is
212 /// the value of the `child` field in this struct. However there is no way221 /// the value of the `child` field in this struct. However there is no way
213 /// to refer to that type here, so we use `var`.222 /// to refer to that type here, so we use `var`.
214 sentinel: var,223 sentinel: anytype,
215 };224 };
216225
217 /// This data structure is used by the Zig language code generation and226 /// This data structure is used by the Zig language code generation and
...@@ -228,15 +237,16 @@ pub const TypeInfo = union(enum) {...@@ -228,15 +237,16 @@ pub const TypeInfo = union(enum) {
228 name: []const u8,237 name: []const u8,
229 offset: ?comptime_int,238 offset: ?comptime_int,
230 field_type: type,239 field_type: type,
231 default_value: var,240 default_value: anytype,
232 };241 };
233242
234 /// This data structure is used by the Zig language code generation and243 /// This data structure is used by the Zig language code generation and
235 /// therefore must be kept in sync with the compiler implementation.244 /// therefore must be kept in sync with the compiler implementation.
236 pub const Struct = struct {245 pub const Struct = struct {
237 layout: ContainerLayout,246 layout: ContainerLayout,
238 fields: []StructField,247 fields: []const StructField,
239 decls: []Declaration,248 decls: []const Declaration,
249 is_tuple: bool,
240 };250 };
241251
242 /// This data structure is used by the Zig language code generation and252 /// This data structure is used by the Zig language code generation and
...@@ -256,12 +266,13 @@ pub const TypeInfo = union(enum) {...@@ -256,12 +266,13 @@ pub const TypeInfo = union(enum) {
256 /// therefore must be kept in sync with the compiler implementation.266 /// therefore must be kept in sync with the compiler implementation.
257 pub const Error = struct {267 pub const Error = struct {
258 name: []const u8,268 name: []const u8,
269 /// This field is ignored when using @Type().
259 value: comptime_int,270 value: comptime_int,
260 };271 };
261272
262 /// This data structure is used by the Zig language code generation and273 /// This data structure is used by the Zig language code generation and
263 /// therefore must be kept in sync with the compiler implementation.274 /// therefore must be kept in sync with the compiler implementation.
264 pub const ErrorSet = ?[]Error;275 pub const ErrorSet = ?[]const Error;
265276
266 /// This data structure is used by the Zig language code generation and277 /// This data structure is used by the Zig language code generation and
267 /// therefore must be kept in sync with the compiler implementation.278 /// therefore must be kept in sync with the compiler implementation.
...@@ -275,8 +286,8 @@ pub const TypeInfo = union(enum) {...@@ -275,8 +286,8 @@ pub const TypeInfo = union(enum) {
275 pub const Enum = struct {286 pub const Enum = struct {
276 layout: ContainerLayout,287 layout: ContainerLayout,
277 tag_type: type,288 tag_type: type,
278 fields: []EnumField,289 fields: []const EnumField,
279 decls: []Declaration,290 decls: []const Declaration,
280 is_exhaustive: bool,291 is_exhaustive: bool,
281 };292 };
282293
...@@ -293,8 +304,8 @@ pub const TypeInfo = union(enum) {...@@ -293,8 +304,8 @@ pub const TypeInfo = union(enum) {
293 pub const Union = struct {304 pub const Union = struct {
294 layout: ContainerLayout,305 layout: ContainerLayout,
295 tag_type: ?type,306 tag_type: ?type,
296 fields: []UnionField,307 fields: []const UnionField,
297 decls: []Declaration,308 decls: []const Declaration,
298 };309 };
299310
300 /// This data structure is used by the Zig language code generation and311 /// This data structure is used by the Zig language code generation and
...@@ -312,7 +323,13 @@ pub const TypeInfo = union(enum) {...@@ -312,7 +323,13 @@ pub const TypeInfo = union(enum) {
312 is_generic: bool,323 is_generic: bool,
313 is_var_args: bool,324 is_var_args: bool,
314 return_type: ?type,325 return_type: ?type,
315 args: []FnArg,326 args: []const FnArg,
327 };
328
329 /// This data structure is used by the Zig language code generation and
330 /// therefore must be kept in sync with the compiler implementation.
331 pub const Frame = struct {
332 function: anytype,
316 };333 };
317334
318 /// This data structure is used by the Zig language code generation and335 /// This data structure is used by the Zig language code generation and
...@@ -352,7 +369,7 @@ pub const TypeInfo = union(enum) {...@@ -352,7 +369,7 @@ pub const TypeInfo = union(enum) {
352 is_export: bool,369 is_export: bool,
353 lib_name: ?[]const u8,370 lib_name: ?[]const u8,
354 return_type: type,371 return_type: type,
355 arg_names: [][]const u8,372 arg_names: []const []const u8,
356373
357 /// This data structure is used by the Zig language code generation and374 /// This data structure is used by the Zig language code generation and
358 /// therefore must be kept in sync with the compiler implementation.375 /// therefore must be kept in sync with the compiler implementation.
...@@ -436,7 +453,7 @@ pub const Version = struct {...@@ -436,7 +453,7 @@ pub const Version = struct {
436 self: Version,453 self: Version,
437 comptime fmt: []const u8,454 comptime fmt: []const u8,
438 options: std.fmt.FormatOptions,455 options: std.fmt.FormatOptions,
439 out_stream: var,456 out_stream: anytype,
440 ) !void {457 ) !void {
441 if (fmt.len == 0) {458 if (fmt.len == 0) {
442 if (self.patch == 0) {459 if (self.patch == 0) {
lib/std/c.zig+15-2
...@@ -27,7 +27,7 @@ pub usingnamespace switch (std.Target.current.os.tag) {...@@ -27,7 +27,7 @@ pub usingnamespace switch (std.Target.current.os.tag) {
27 else => struct {},27 else => struct {},
28};28};
2929
30pub fn getErrno(rc: var) u16 {30pub fn getErrno(rc: anytype) u16 {
31 if (rc == -1) {31 if (rc == -1) {
32 return @intCast(u16, _errno().*);32 return @intCast(u16, _errno().*);
33 } else {33 } else {
...@@ -73,7 +73,6 @@ pub extern "c" fn abort() noreturn;...@@ -73,7 +73,6 @@ pub extern "c" fn abort() noreturn;
73pub extern "c" fn exit(code: c_int) noreturn;73pub extern "c" fn exit(code: c_int) noreturn;
74pub extern "c" fn isatty(fd: fd_t) c_int;74pub extern "c" fn isatty(fd: fd_t) c_int;
75pub extern "c" fn close(fd: fd_t) c_int;75pub extern "c" fn close(fd: fd_t) c_int;
76pub extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, stat_buf: *Stat, flags: u32) c_int;
77pub extern "c" fn lseek(fd: fd_t, offset: off_t, whence: c_int) off_t;76pub extern "c" fn lseek(fd: fd_t, offset: off_t, whence: c_int) off_t;
78pub extern "c" fn open(path: [*:0]const u8, oflag: c_uint, ...) c_int;77pub extern "c" fn open(path: [*:0]const u8, oflag: c_uint, ...) c_int;
79pub extern "c" fn openat(fd: c_int, path: [*:0]const u8, oflag: c_uint, ...) c_int;78pub extern "c" fn openat(fd: c_int, path: [*:0]const u8, oflag: c_uint, ...) c_int;
...@@ -102,6 +101,7 @@ pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;...@@ -102,6 +101,7 @@ pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
102pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;101pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;
103pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int;102pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int;
104pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int;103pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int;
104pub extern "c" fn symlinkat(oldpath: [*:0]const u8, newdirfd: fd_t, newpath: [*:0]const u8) c_int;
105pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int;105pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int;
106pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int;106pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int;
107pub extern "c" fn chdir(path: [*:0]const u8) c_int;107pub extern "c" fn chdir(path: [*:0]const u8) c_int;
...@@ -115,9 +115,11 @@ pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias b...@@ -115,9 +115,11 @@ pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias b
115pub usingnamespace switch (builtin.os.tag) {115pub usingnamespace switch (builtin.os.tag) {
116 .macosx, .ios, .watchos, .tvos => struct {116 .macosx, .ios, .watchos, .tvos => struct {
117 pub const realpath = @"realpath$DARWIN_EXTSN";117 pub const realpath = @"realpath$DARWIN_EXTSN";
118 pub const fstatat = @"fstatat$INODE64";
118 },119 },
119 else => struct {120 else => struct {
120 pub extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8;121 pub extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8;
122 pub extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, stat_buf: *Stat, flags: u32) c_int;
121 },123 },
122};124};
123125
...@@ -231,6 +233,17 @@ pub extern "c" fn setuid(uid: c_uint) c_int;...@@ -231,6 +233,17 @@ pub extern "c" fn setuid(uid: c_uint) c_int;
231233
232pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void;234pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void;
233pub extern "c" fn malloc(usize) ?*c_void;235pub extern "c" fn malloc(usize) ?*c_void;
236
237pub usingnamespace switch (builtin.os.tag) {
238 .linux, .freebsd, .kfreebsd, .netbsd, .openbsd => struct {
239 pub extern "c" fn malloc_usable_size(?*const c_void) usize;
240 },
241 .macosx, .ios, .watchos, .tvos => struct {
242 pub extern "c" fn malloc_size(?*const c_void) usize;
243 },
244 else => struct {},
245};
246
234pub extern "c" fn realloc(?*c_void, usize) ?*c_void;247pub extern "c" fn realloc(?*c_void, usize) ?*c_void;
235pub extern "c" fn free(*c_void) void;248pub extern "c" fn free(*c_void) void;
236pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int;249pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int;
lib/std/c/ast.zig+7-7
...@@ -64,7 +64,7 @@ pub const Error = union(enum) {...@@ -64,7 +64,7 @@ pub const Error = union(enum) {
64 NothingDeclared: SimpleError("declaration doesn't declare anything"),64 NothingDeclared: SimpleError("declaration doesn't declare anything"),
65 QualifierIgnored: SingleTokenError("qualifier '{}' ignored"),65 QualifierIgnored: SingleTokenError("qualifier '{}' ignored"),
6666
67 pub fn render(self: *const Error, tree: *Tree, stream: var) !void {67 pub fn render(self: *const Error, tree: *Tree, stream: anytype) !void {
68 switch (self.*) {68 switch (self.*) {
69 .InvalidToken => |*x| return x.render(tree, stream),69 .InvalidToken => |*x| return x.render(tree, stream),
70 .ExpectedToken => |*x| return x.render(tree, stream),70 .ExpectedToken => |*x| return x.render(tree, stream),
...@@ -114,7 +114,7 @@ pub const Error = union(enum) {...@@ -114,7 +114,7 @@ pub const Error = union(enum) {
114 token: TokenIndex,114 token: TokenIndex,
115 expected_id: @TagType(Token.Id),115 expected_id: @TagType(Token.Id),
116116
117 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: var) !void {117 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void {
118 const found_token = tree.tokens.at(self.token);118 const found_token = tree.tokens.at(self.token);
119 if (found_token.id == .Invalid) {119 if (found_token.id == .Invalid) {
120 return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()});120 return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()});
...@@ -129,7 +129,7 @@ pub const Error = union(enum) {...@@ -129,7 +129,7 @@ pub const Error = union(enum) {
129 token: TokenIndex,129 token: TokenIndex,
130 type_spec: *Node.TypeSpec,130 type_spec: *Node.TypeSpec,
131131
132 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: var) !void {132 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void {
133 try stream.write("invalid type specifier '");133 try stream.write("invalid type specifier '");
134 try type_spec.spec.print(tree, stream);134 try type_spec.spec.print(tree, stream);
135 const token_name = tree.tokens.at(self.token).id.symbol();135 const token_name = tree.tokens.at(self.token).id.symbol();
...@@ -141,7 +141,7 @@ pub const Error = union(enum) {...@@ -141,7 +141,7 @@ pub const Error = union(enum) {
141 kw: TokenIndex,141 kw: TokenIndex,
142 name: TokenIndex,142 name: TokenIndex,
143143
144 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: var) !void {144 pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void {
145 return stream.print("must use '{}' tag to refer to type '{}'", .{ tree.slice(kw), tree.slice(name) });145 return stream.print("must use '{}' tag to refer to type '{}'", .{ tree.slice(kw), tree.slice(name) });
146 }146 }
147 };147 };
...@@ -150,7 +150,7 @@ pub const Error = union(enum) {...@@ -150,7 +150,7 @@ pub const Error = union(enum) {
150 return struct {150 return struct {
151 token: TokenIndex,151 token: TokenIndex,
152152
153 pub fn render(self: *const @This(), tree: *Tree, stream: var) !void {153 pub fn render(self: *const @This(), tree: *Tree, stream: anytype) !void {
154 const actual_token = tree.tokens.at(self.token);154 const actual_token = tree.tokens.at(self.token);
155 return stream.print(msg, .{actual_token.id.symbol()});155 return stream.print(msg, .{actual_token.id.symbol()});
156 }156 }
...@@ -163,7 +163,7 @@ pub const Error = union(enum) {...@@ -163,7 +163,7 @@ pub const Error = union(enum) {
163163
164 token: TokenIndex,164 token: TokenIndex,
165165
166 pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void {166 pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: anytype) !void {
167 return stream.write(msg);167 return stream.write(msg);
168 }168 }
169 };169 };
...@@ -317,7 +317,7 @@ pub const Node = struct {...@@ -317,7 +317,7 @@ pub const Node = struct {
317 sym_type: *Type,317 sym_type: *Type,
318 },318 },
319319
320 pub fn print(self: *@This(), self: *const @This(), tree: *Tree, stream: var) !void {320 pub fn print(self: *@This(), self: *const @This(), tree: *Tree, stream: anytype) !void {
321 switch (self.spec) {321 switch (self.spec) {
322 .None => unreachable,322 .None => unreachable,
323 .Void => |index| try stream.write(tree.slice(index)),323 .Void => |index| try stream.write(tree.slice(index)),
lib/std/c/darwin.zig+1
...@@ -16,6 +16,7 @@ pub extern "c" fn @"realpath$DARWIN_EXTSN"(noalias file_name: [*:0]const u8, noa...@@ -16,6 +16,7 @@ pub extern "c" fn @"realpath$DARWIN_EXTSN"(noalias file_name: [*:0]const u8, noa
1616
17pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize;17pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize;
18pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int;18pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int;
19pub extern "c" fn @"fstatat$INODE64"(dirfd: fd_t, path_name: [*:0]const u8, buf: *Stat, flags: u32) c_int;
1920
20pub extern "c" fn mach_absolute_time() u64;21pub extern "c" fn mach_absolute_time() u64;
21pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;22pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;
lib/std/c/tokenizer.zig+50-50
...@@ -278,62 +278,62 @@ pub const Token = struct {...@@ -278,62 +278,62 @@ pub const Token = struct {
278278
279 // TODO extensions279 // TODO extensions
280 pub const keywords = std.ComptimeStringMap(Id, .{280 pub const keywords = std.ComptimeStringMap(Id, .{
281 .{"auto", .Keyword_auto},281 .{ "auto", .Keyword_auto },
282 .{"break", .Keyword_break},282 .{ "break", .Keyword_break },
283 .{"case", .Keyword_case},283 .{ "case", .Keyword_case },
284 .{"char", .Keyword_char},284 .{ "char", .Keyword_char },
285 .{"const", .Keyword_const},285 .{ "const", .Keyword_const },
286 .{"continue", .Keyword_continue},286 .{ "continue", .Keyword_continue },
287 .{"default", .Keyword_default},287 .{ "default", .Keyword_default },
288 .{"do", .Keyword_do},288 .{ "do", .Keyword_do },
289 .{"double", .Keyword_double},289 .{ "double", .Keyword_double },
290 .{"else", .Keyword_else},290 .{ "else", .Keyword_else },
291 .{"enum", .Keyword_enum},291 .{ "enum", .Keyword_enum },
292 .{"extern", .Keyword_extern},292 .{ "extern", .Keyword_extern },
293 .{"float", .Keyword_float},293 .{ "float", .Keyword_float },
294 .{"for", .Keyword_for},294 .{ "for", .Keyword_for },
295 .{"goto", .Keyword_goto},295 .{ "goto", .Keyword_goto },
296 .{"if", .Keyword_if},296 .{ "if", .Keyword_if },
297 .{"int", .Keyword_int},297 .{ "int", .Keyword_int },
298 .{"long", .Keyword_long},298 .{ "long", .Keyword_long },
299 .{"register", .Keyword_register},299 .{ "register", .Keyword_register },
300 .{"return", .Keyword_return},300 .{ "return", .Keyword_return },
301 .{"short", .Keyword_short},301 .{ "short", .Keyword_short },
302 .{"signed", .Keyword_signed},302 .{ "signed", .Keyword_signed },
303 .{"sizeof", .Keyword_sizeof},303 .{ "sizeof", .Keyword_sizeof },
304 .{"static", .Keyword_static},304 .{ "static", .Keyword_static },
305 .{"struct", .Keyword_struct},305 .{ "struct", .Keyword_struct },
306 .{"switch", .Keyword_switch},306 .{ "switch", .Keyword_switch },
307 .{"typedef", .Keyword_typedef},307 .{ "typedef", .Keyword_typedef },
308 .{"union", .Keyword_union},308 .{ "union", .Keyword_union },
309 .{"unsigned", .Keyword_unsigned},309 .{ "unsigned", .Keyword_unsigned },
310 .{"void", .Keyword_void},310 .{ "void", .Keyword_void },
311 .{"volatile", .Keyword_volatile},311 .{ "volatile", .Keyword_volatile },
312 .{"while", .Keyword_while},312 .{ "while", .Keyword_while },
313313
314 // ISO C99314 // ISO C99
315 .{"_Bool", .Keyword_bool},315 .{ "_Bool", .Keyword_bool },
316 .{"_Complex", .Keyword_complex},316 .{ "_Complex", .Keyword_complex },
317 .{"_Imaginary", .Keyword_imaginary},317 .{ "_Imaginary", .Keyword_imaginary },
318 .{"inline", .Keyword_inline},318 .{ "inline", .Keyword_inline },
319 .{"restrict", .Keyword_restrict},319 .{ "restrict", .Keyword_restrict },
320320
321 // ISO C11321 // ISO C11
322 .{"_Alignas", .Keyword_alignas},322 .{ "_Alignas", .Keyword_alignas },
323 .{"_Alignof", .Keyword_alignof},323 .{ "_Alignof", .Keyword_alignof },
324 .{"_Atomic", .Keyword_atomic},324 .{ "_Atomic", .Keyword_atomic },
325 .{"_Generic", .Keyword_generic},325 .{ "_Generic", .Keyword_generic },
326 .{"_Noreturn", .Keyword_noreturn},326 .{ "_Noreturn", .Keyword_noreturn },
327 .{"_Static_assert", .Keyword_static_assert},327 .{ "_Static_assert", .Keyword_static_assert },
328 .{"_Thread_local", .Keyword_thread_local},328 .{ "_Thread_local", .Keyword_thread_local },
329329
330 // Preprocessor directives330 // Preprocessor directives
331 .{"include", .Keyword_include},331 .{ "include", .Keyword_include },
332 .{"define", .Keyword_define},332 .{ "define", .Keyword_define },
333 .{"ifdef", .Keyword_ifdef},333 .{ "ifdef", .Keyword_ifdef },
334 .{"ifndef", .Keyword_ifndef},334 .{ "ifndef", .Keyword_ifndef },
335 .{"error", .Keyword_error},335 .{ "error", .Keyword_error },
336 .{"pragma", .Keyword_pragma},336 .{ "pragma", .Keyword_pragma },
337 });337 });
338338
339 // TODO do this in the preprocessor339 // TODO do this in the preprocessor
lib/std/cache_hash.zig+2-2
...@@ -70,7 +70,7 @@ pub const CacheHash = struct {...@@ -70,7 +70,7 @@ pub const CacheHash = struct {
7070
71 /// Convert the input value into bytes and record it as a dependency of the71 /// Convert the input value into bytes and record it as a dependency of the
72 /// process being cached72 /// process being cached
73 pub fn add(self: *CacheHash, val: var) void {73 pub fn add(self: *CacheHash, val: anytype) void {
74 assert(self.manifest_file == null);74 assert(self.manifest_file == null);
7575
76 const valPtr = switch (@typeInfo(@TypeOf(val))) {76 const valPtr = switch (@typeInfo(@TypeOf(val))) {
...@@ -207,7 +207,7 @@ pub const CacheHash = struct {...@@ -207,7 +207,7 @@ pub const CacheHash = struct {
207 }207 }
208208
209 if (cache_hash_file.path == null) {209 if (cache_hash_file.path == null) {
210 cache_hash_file.path = try mem.dupe(self.allocator, u8, file_path);210 cache_hash_file.path = try self.allocator.dupe(u8, file_path);
211 }211 }
212212
213 const this_file = fs.cwd().openFile(cache_hash_file.path.?, .{ .read = true }) catch {213 const this_file = fs.cwd().openFile(cache_hash_file.path.?, .{ .read = true }) catch {
lib/std/comptime_string_map.zig+3-3
...@@ -8,7 +8,7 @@ const mem = std.mem;...@@ -8,7 +8,7 @@ const mem = std.mem;
8/// `kvs` expects a list literal containing list literals or an array/slice of structs8/// `kvs` expects a list literal containing list literals or an array/slice of structs
9/// where `.@"0"` is the `[]const u8` key and `.@"1"` is the associated value of type `V`.9/// where `.@"0"` is the `[]const u8` key and `.@"1"` is the associated value of type `V`.
10/// TODO: https://github.com/ziglang/zig/issues/433510/// TODO: https://github.com/ziglang/zig/issues/4335
11pub fn ComptimeStringMap(comptime V: type, comptime kvs: var) type {11pub fn ComptimeStringMap(comptime V: type, comptime kvs: anytype) type {
12 const precomputed = comptime blk: {12 const precomputed = comptime blk: {
13 @setEvalBranchQuota(2000);13 @setEvalBranchQuota(2000);
14 const KV = struct {14 const KV = struct {
...@@ -126,7 +126,7 @@ test "ComptimeStringMap slice of structs" {...@@ -126,7 +126,7 @@ test "ComptimeStringMap slice of structs" {
126 testMap(map);126 testMap(map);
127}127}
128128
129fn testMap(comptime map: var) void {129fn testMap(comptime map: anytype) void {
130 std.testing.expectEqual(TestEnum.A, map.get("have").?);130 std.testing.expectEqual(TestEnum.A, map.get("have").?);
131 std.testing.expectEqual(TestEnum.B, map.get("nothing").?);131 std.testing.expectEqual(TestEnum.B, map.get("nothing").?);
132 std.testing.expect(null == map.get("missing"));132 std.testing.expect(null == map.get("missing"));
...@@ -165,7 +165,7 @@ test "ComptimeStringMap void value type, list literal of list literals" {...@@ -165,7 +165,7 @@ test "ComptimeStringMap void value type, list literal of list literals" {
165 testSet(map);165 testSet(map);
166}166}
167167
168fn testSet(comptime map: var) void {168fn testSet(comptime map: anytype) void {
169 std.testing.expectEqual({}, map.get("have").?);169 std.testing.expectEqual({}, map.get("have").?);
170 std.testing.expectEqual({}, map.get("nothing").?);170 std.testing.expectEqual({}, map.get("nothing").?);
171 std.testing.expect(null == map.get("missing"));171 std.testing.expect(null == map.get("missing"));
lib/std/crypto/benchmark.zig+6-18
...@@ -29,7 +29,7 @@ const hashes = [_]Crypto{...@@ -29,7 +29,7 @@ const hashes = [_]Crypto{
29 Crypto{ .ty = crypto.Blake3, .name = "blake3" },29 Crypto{ .ty = crypto.Blake3, .name = "blake3" },
30};30};
3131
32pub fn benchmarkHash(comptime Hash: var, comptime bytes: comptime_int) !u64 {32pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 {
33 var h = Hash.init();33 var h = Hash.init();
3434
35 var block: [Hash.digest_length]u8 = undefined;35 var block: [Hash.digest_length]u8 = undefined;
...@@ -56,7 +56,7 @@ const macs = [_]Crypto{...@@ -56,7 +56,7 @@ const macs = [_]Crypto{
56 Crypto{ .ty = crypto.HmacSha256, .name = "hmac-sha256" },56 Crypto{ .ty = crypto.HmacSha256, .name = "hmac-sha256" },
57};57};
5858
59pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 {59pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
60 std.debug.assert(32 >= Mac.mac_length and 32 >= Mac.minimum_key_length);60 std.debug.assert(32 >= Mac.mac_length and 32 >= Mac.minimum_key_length);
6161
62 var in: [1 * MiB]u8 = undefined;62 var in: [1 * MiB]u8 = undefined;
...@@ -81,7 +81,7 @@ pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 {...@@ -81,7 +81,7 @@ pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 {
8181
82const exchanges = [_]Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }};82const exchanges = [_]Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }};
8383
84pub fn benchmarkKeyExchange(comptime DhKeyExchange: var, comptime exchange_count: comptime_int) !u64 {84pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_count: comptime_int) !u64 {
85 std.debug.assert(DhKeyExchange.minimum_key_length >= DhKeyExchange.secret_length);85 std.debug.assert(DhKeyExchange.minimum_key_length >= DhKeyExchange.secret_length);
8686
87 var in: [DhKeyExchange.minimum_key_length]u8 = undefined;87 var in: [DhKeyExchange.minimum_key_length]u8 = undefined;
...@@ -123,15 +123,6 @@ fn mode(comptime x: comptime_int) comptime_int {...@@ -123,15 +123,6 @@ fn mode(comptime x: comptime_int) comptime_int {
123 return if (builtin.mode == .Debug) x / 64 else x;123 return if (builtin.mode == .Debug) x / 64 else x;
124}124}
125125
126// TODO(#1358): Replace with builtin formatted padding when available.
127fn printPad(stdout: var, s: []const u8) !void {
128 var i: usize = 0;
129 while (i < 12 - s.len) : (i += 1) {
130 try stdout.print(" ", .{});
131 }
132 try stdout.print("{}", .{s});
133}
134
135pub fn main() !void {126pub fn main() !void {
136 const stdout = std.io.getStdOut().outStream();127 const stdout = std.io.getStdOut().outStream();
137128
...@@ -175,24 +166,21 @@ pub fn main() !void {...@@ -175,24 +166,21 @@ pub fn main() !void {
175 inline for (hashes) |H| {166 inline for (hashes) |H| {
176 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {167 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
177 const throughput = try benchmarkHash(H.ty, mode(32 * MiB));168 const throughput = try benchmarkHash(H.ty, mode(32 * MiB));
178 try printPad(stdout, H.name);169 try stdout.print("{:>11}: {:5} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
179 try stdout.print(": {} MiB/s\n", .{throughput / (1 * MiB)});
180 }170 }
181 }171 }
182172
183 inline for (macs) |M| {173 inline for (macs) |M| {
184 if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {174 if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
185 const throughput = try benchmarkMac(M.ty, mode(128 * MiB));175 const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
186 try printPad(stdout, M.name);176 try stdout.print("{:>11}: {:5} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
187 try stdout.print(": {} MiB/s\n", .{throughput / (1 * MiB)});
188 }177 }
189 }178 }
190179
191 inline for (exchanges) |E| {180 inline for (exchanges) |E| {
192 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {181 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
193 const throughput = try benchmarkKeyExchange(E.ty, mode(1000));182 const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
194 try printPad(stdout, E.name);183 try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput });
195 try stdout.print(": {} exchanges/s\n", .{throughput});
196 }184 }
197 }185 }
198}186}
lib/std/crypto/test.zig+1-1
...@@ -4,7 +4,7 @@ const mem = std.mem;...@@ -4,7 +4,7 @@ const mem = std.mem;
4const fmt = std.fmt;4const fmt = std.fmt;
55
6// Hash using the specified hasher `H` asserting `expected == H(input)`.6// Hash using the specified hasher `H` asserting `expected == H(input)`.
7pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, input: []const u8) void {7pub fn assertEqualHash(comptime Hasher: anytype, comptime expected: []const u8, input: []const u8) void {
8 var h: [expected.len / 2]u8 = undefined;8 var h: [expected.len / 2]u8 = undefined;
9 Hasher.hash(input, h[0..]);9 Hasher.hash(input, h[0..]);
1010
lib/std/debug.zig+30-40
...@@ -50,33 +50,21 @@ pub const LineInfo = struct {...@@ -50,33 +50,21 @@ pub const LineInfo = struct {
50 }50 }
51};51};
5252
53/// Tries to write to stderr, unbuffered, and ignores any error returned.
54/// Does not append a newline.
55var stderr_file: File = undefined;
56var stderr_file_writer: File.Writer = undefined;
57
58var stderr_stream: ?*File.OutStream = null;
59var stderr_mutex = std.Mutex.init();53var stderr_mutex = std.Mutex.init();
6054
61pub fn warn(comptime fmt: []const u8, args: var) void {55/// Deprecated. Use `std.log` functions for logging or `std.debug.print` for
56/// "printf debugging".
57pub const warn = print;
58
59/// Print to stderr, unbuffered, and silently returning on failure. Intended
60/// for use in "printf debugging." Use `std.log` functions for proper logging.
61pub fn print(comptime fmt: []const u8, args: anytype) void {
62 const held = stderr_mutex.acquire();62 const held = stderr_mutex.acquire();
63 defer held.release();63 defer held.release();
64 const stderr = getStderrStream();64 const stderr = io.getStdErr().writer();
65 nosuspend stderr.print(fmt, args) catch return;65 nosuspend stderr.print(fmt, args) catch return;
66}66}
6767
68pub fn getStderrStream() *File.OutStream {
69 if (stderr_stream) |st| {
70 return st;
71 } else {
72 stderr_file = io.getStdErr();
73 stderr_file_writer = stderr_file.outStream();
74 const st = &stderr_file_writer;
75 stderr_stream = st;
76 return st;
77 }
78}
79
80pub fn getStderrMutex() *std.Mutex {68pub fn getStderrMutex() *std.Mutex {
81 return &stderr_mutex;69 return &stderr_mutex;
82}70}
...@@ -99,6 +87,7 @@ pub fn detectTTYConfig() TTY.Config {...@@ -99,6 +87,7 @@ pub fn detectTTYConfig() TTY.Config {
99 if (process.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| {87 if (process.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| {
100 return .escape_codes;88 return .escape_codes;
101 } else |_| {89 } else |_| {
90 const stderr_file = io.getStdErr();
102 if (stderr_file.supportsAnsiEscapeCodes()) {91 if (stderr_file.supportsAnsiEscapeCodes()) {
103 return .escape_codes;92 return .escape_codes;
104 } else if (builtin.os.tag == .windows and stderr_file.isTty()) {93 } else if (builtin.os.tag == .windows and stderr_file.isTty()) {
...@@ -113,7 +102,7 @@ pub fn detectTTYConfig() TTY.Config {...@@ -113,7 +102,7 @@ pub fn detectTTYConfig() TTY.Config {
113/// TODO multithreaded awareness102/// TODO multithreaded awareness
114pub fn dumpCurrentStackTrace(start_addr: ?usize) void {103pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
115 nosuspend {104 nosuspend {
116 const stderr = getStderrStream();105 const stderr = io.getStdErr().writer();
117 if (builtin.strip_debug_info) {106 if (builtin.strip_debug_info) {
118 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;107 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
119 return;108 return;
...@@ -134,7 +123,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -134,7 +123,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
134/// TODO multithreaded awareness123/// TODO multithreaded awareness
135pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {124pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
136 nosuspend {125 nosuspend {
137 const stderr = getStderrStream();126 const stderr = io.getStdErr().writer();
138 if (builtin.strip_debug_info) {127 if (builtin.strip_debug_info) {
139 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;128 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
140 return;129 return;
...@@ -204,7 +193,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace...@@ -204,7 +193,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace
204/// TODO multithreaded awareness193/// TODO multithreaded awareness
205pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void {194pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void {
206 nosuspend {195 nosuspend {
207 const stderr = getStderrStream();196 const stderr = io.getStdErr().writer();
208 if (builtin.strip_debug_info) {197 if (builtin.strip_debug_info) {
209 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;198 stderr.print("Unable to dump stack trace: debug info stripped\n", .{}) catch return;
210 return;199 return;
...@@ -234,7 +223,7 @@ pub fn assert(ok: bool) void {...@@ -234,7 +223,7 @@ pub fn assert(ok: bool) void {
234 if (!ok) unreachable; // assertion failure223 if (!ok) unreachable; // assertion failure
235}224}
236225
237pub fn panic(comptime format: []const u8, args: var) noreturn {226pub fn panic(comptime format: []const u8, args: anytype) noreturn {
238 @setCold(true);227 @setCold(true);
239 // TODO: remove conditional once wasi / LLVM defines __builtin_return_address228 // TODO: remove conditional once wasi / LLVM defines __builtin_return_address
240 const first_trace_addr = if (builtin.os.tag == .wasi) null else @returnAddress();229 const first_trace_addr = if (builtin.os.tag == .wasi) null else @returnAddress();
...@@ -252,7 +241,7 @@ var panic_mutex = std.Mutex.init();...@@ -252,7 +241,7 @@ var panic_mutex = std.Mutex.init();
252/// This is used to catch and handle panics triggered by the panic handler.241/// This is used to catch and handle panics triggered by the panic handler.
253threadlocal var panic_stage: usize = 0;242threadlocal var panic_stage: usize = 0;
254243
255pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn {244pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: anytype) noreturn {
256 @setCold(true);245 @setCold(true);
257246
258 if (enable_segfault_handler) {247 if (enable_segfault_handler) {
...@@ -272,7 +261,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c...@@ -272,7 +261,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
272 const held = panic_mutex.acquire();261 const held = panic_mutex.acquire();
273 defer held.release();262 defer held.release();
274263
275 const stderr = getStderrStream();264 const stderr = io.getStdErr().writer();
276 stderr.print(format ++ "\n", args) catch os.abort();265 stderr.print(format ++ "\n", args) catch os.abort();
277 if (trace) |t| {266 if (trace) |t| {
278 dumpStackTrace(t.*);267 dumpStackTrace(t.*);
...@@ -297,7 +286,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c...@@ -297,7 +286,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
297 // A panic happened while trying to print a previous panic message,286 // A panic happened while trying to print a previous panic message,
298 // we're still holding the mutex but that's fine as we're going to287 // we're still holding the mutex but that's fine as we're going to
299 // call abort()288 // call abort()
300 const stderr = getStderrStream();289 const stderr = io.getStdErr().writer();
301 stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();290 stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
302 },291 },
303 else => {292 else => {
...@@ -317,7 +306,7 @@ const RESET = "\x1b[0m";...@@ -317,7 +306,7 @@ const RESET = "\x1b[0m";
317306
318pub fn writeStackTrace(307pub fn writeStackTrace(
319 stack_trace: builtin.StackTrace,308 stack_trace: builtin.StackTrace,
320 out_stream: var,309 out_stream: anytype,
321 allocator: *mem.Allocator,310 allocator: *mem.Allocator,
322 debug_info: *DebugInfo,311 debug_info: *DebugInfo,
323 tty_config: TTY.Config,312 tty_config: TTY.Config,
...@@ -395,7 +384,7 @@ pub const StackIterator = struct {...@@ -395,7 +384,7 @@ pub const StackIterator = struct {
395};384};
396385
397pub fn writeCurrentStackTrace(386pub fn writeCurrentStackTrace(
398 out_stream: var,387 out_stream: anytype,
399 debug_info: *DebugInfo,388 debug_info: *DebugInfo,
400 tty_config: TTY.Config,389 tty_config: TTY.Config,
401 start_addr: ?usize,390 start_addr: ?usize,
...@@ -410,7 +399,7 @@ pub fn writeCurrentStackTrace(...@@ -410,7 +399,7 @@ pub fn writeCurrentStackTrace(
410}399}
411400
412pub fn writeCurrentStackTraceWindows(401pub fn writeCurrentStackTraceWindows(
413 out_stream: var,402 out_stream: anytype,
414 debug_info: *DebugInfo,403 debug_info: *DebugInfo,
415 tty_config: TTY.Config,404 tty_config: TTY.Config,
416 start_addr: ?usize,405 start_addr: ?usize,
...@@ -446,7 +435,7 @@ pub const TTY = struct {...@@ -446,7 +435,7 @@ pub const TTY = struct {
446 // TODO give this a payload of file handle435 // TODO give this a payload of file handle
447 windows_api,436 windows_api,
448437
449 fn setColor(conf: Config, out_stream: var, color: Color) void {438 fn setColor(conf: Config, out_stream: anytype, color: Color) void {
450 nosuspend switch (conf) {439 nosuspend switch (conf) {
451 .no_color => return,440 .no_color => return,
452 .escape_codes => switch (color) {441 .escape_codes => switch (color) {
...@@ -458,6 +447,7 @@ pub const TTY = struct {...@@ -458,6 +447,7 @@ pub const TTY = struct {
458 .Reset => out_stream.writeAll(RESET) catch return,447 .Reset => out_stream.writeAll(RESET) catch return,
459 },448 },
460 .windows_api => if (builtin.os.tag == .windows) {449 .windows_api => if (builtin.os.tag == .windows) {
450 const stderr_file = io.getStdErr();
461 const S = struct {451 const S = struct {
462 var attrs: windows.WORD = undefined;452 var attrs: windows.WORD = undefined;
463 var init_attrs = false;453 var init_attrs = false;
...@@ -565,7 +555,7 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach...@@ -565,7 +555,7 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach
565}555}
566556
567/// TODO resources https://github.com/ziglang/zig/issues/4353557/// TODO resources https://github.com/ziglang/zig/issues/4353
568pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {558pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: anytype, address: usize, tty_config: TTY.Config) !void {
569 const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {559 const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {
570 error.MissingDebugInfo, error.InvalidDebugInfo => {560 error.MissingDebugInfo, error.InvalidDebugInfo => {
571 return printLineInfo(561 return printLineInfo(
...@@ -596,13 +586,13 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: us...@@ -596,13 +586,13 @@ pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: us
596}586}
597587
598fn printLineInfo(588fn printLineInfo(
599 out_stream: var,589 out_stream: anytype,
600 line_info: ?LineInfo,590 line_info: ?LineInfo,
601 address: usize,591 address: usize,
602 symbol_name: []const u8,592 symbol_name: []const u8,
603 compile_unit_name: []const u8,593 compile_unit_name: []const u8,
604 tty_config: TTY.Config,594 tty_config: TTY.Config,
605 comptime printLineFromFile: var,595 comptime printLineFromFile: anytype,
606) !void {596) !void {
607 nosuspend {597 nosuspend {
608 tty_config.setColor(out_stream, .White);598 tty_config.setColor(out_stream, .White);
...@@ -830,7 +820,7 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf...@@ -830,7 +820,7 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
830 }820 }
831}821}
832822
833fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {823fn readSparseBitVector(stream: anytype, allocator: *mem.Allocator) ![]usize {
834 const num_words = try stream.readIntLittle(u32);824 const num_words = try stream.readIntLittle(u32);
835 var word_i: usize = 0;825 var word_i: usize = 0;
836 var list = ArrayList(usize).init(allocator);826 var list = ArrayList(usize).init(allocator);
...@@ -1014,7 +1004,7 @@ fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugI...@@ -1014,7 +1004,7 @@ fn readMachODebugInfo(allocator: *mem.Allocator, macho_file: File) !ModuleDebugI
1014 };1004 };
1015}1005}
10161006
1017fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {1007fn printLineFromFileAnyOs(out_stream: anytype, line_info: LineInfo) !void {
1018 // Need this to always block even in async I/O mode, because this could potentially1008 // Need this to always block even in async I/O mode, because this could potentially
1019 // be called from e.g. the event loop code crashing.1009 // be called from e.g. the event loop code crashing.
1020 var f = try fs.cwd().openFile(line_info.file_name, .{ .intended_io_mode = .blocking });1010 var f = try fs.cwd().openFile(line_info.file_name, .{ .intended_io_mode = .blocking });
...@@ -1142,7 +1132,7 @@ pub const DebugInfo = struct {...@@ -1142,7 +1132,7 @@ pub const DebugInfo = struct {
1142 const seg_end = seg_start + segment_cmd.vmsize;1132 const seg_end = seg_start + segment_cmd.vmsize;
11431133
1144 if (rebased_address >= seg_start and rebased_address < seg_end) {1134 if (rebased_address >= seg_start and rebased_address < seg_end) {
1145 if (self.address_map.getValue(base_address)) |obj_di| {1135 if (self.address_map.get(base_address)) |obj_di| {
1146 return obj_di;1136 return obj_di;
1147 }1137 }
11481138
...@@ -1214,7 +1204,7 @@ pub const DebugInfo = struct {...@@ -1214,7 +1204,7 @@ pub const DebugInfo = struct {
1214 const seg_end = seg_start + info.SizeOfImage;1204 const seg_end = seg_start + info.SizeOfImage;
12151205
1216 if (address >= seg_start and address < seg_end) {1206 if (address >= seg_start and address < seg_end) {
1217 if (self.address_map.getValue(seg_start)) |obj_di| {1207 if (self.address_map.get(seg_start)) |obj_di| {
1218 return obj_di;1208 return obj_di;
1219 }1209 }
12201210
...@@ -1288,7 +1278,7 @@ pub const DebugInfo = struct {...@@ -1288,7 +1278,7 @@ pub const DebugInfo = struct {
1288 else => return error.MissingDebugInfo,1278 else => return error.MissingDebugInfo,
1289 }1279 }
12901280
1291 if (self.address_map.getValue(ctx.base_address)) |obj_di| {1281 if (self.address_map.get(ctx.base_address)) |obj_di| {
1292 return obj_di;1282 return obj_di;
1293 }1283 }
12941284
...@@ -1451,7 +1441,7 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) {...@@ -1451,7 +1441,7 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) {
1451 const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]);1441 const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]);
14521442
1453 // Check if its debug infos are already in the cache1443 // Check if its debug infos are already in the cache
1454 var o_file_di = self.ofiles.getValue(o_file_path) orelse1444 var o_file_di = self.ofiles.get(o_file_path) orelse
1455 (self.loadOFile(o_file_path) catch |err| switch (err) {1445 (self.loadOFile(o_file_path) catch |err| switch (err) {
1456 error.FileNotFound,1446 error.FileNotFound,
1457 error.MissingDebugInfo,1447 error.MissingDebugInfo,
lib/std/debug/leb128.zig+243-109
...@@ -1,171 +1,211 @@...@@ -1,171 +1,211 @@
1const std = @import("std");1const std = @import("std");
2const testing = std.testing;2const testing = std.testing;
33
4pub fn readULEB128(comptime T: type, in_stream: var) !T {4/// Read a single unsigned LEB128 value from the given reader as type T,
5 const ShiftT = std.meta.Int(false, std.math.log2(T.bit_count));5/// or error.Overflow if the value cannot fit.
6pub fn readULEB128(comptime T: type, reader: anytype) !T {
7 const U = if (T.bit_count < 8) u8 else T;
8 const ShiftT = std.math.Log2Int(U);
69
7 var result: T = 0;10 const max_group = (U.bit_count + 6) / 7;
8 var shift: usize = 0;
911
10 while (true) {12 var value = @as(U, 0);
11 const byte = try in_stream.readByte();13 var group = @as(ShiftT, 0);
12
13 if (shift > T.bit_count)
14 return error.Overflow;
15
16 var operand: T = undefined;
17 if (@shlWithOverflow(T, byte & 0x7f, @intCast(ShiftT, shift), &operand))
18 return error.Overflow;
1914
20 result |= operand;15 while (group < max_group) : (group += 1) {
16 const byte = try reader.readByte();
17 var temp = @as(U, byte & 0x7f);
2118
22 if ((byte & 0x80) == 0)19 if (@shlWithOverflow(U, temp, group * 7, &temp)) return error.Overflow;
23 return result;
2420
25 shift += 7;21 value |= temp;
22 if (byte & 0x80 == 0) break;
23 } else {
24 return error.Overflow;
26 }25 }
27}
28
29pub fn readULEB128Mem(comptime T: type, ptr: *[*]const u8) !T {
30 const ShiftT = std.meta.Int(false, std.math.log2(T.bit_count));
3126
32 var result: T = 0;27 // only applies in the case that we extended to u8
33 var shift: usize = 0;28 if (U != T) {
34 var i: usize = 0;29 if (value > std.math.maxInt(T)) return error.Overflow;
3530 }
36 while (true) : (i += 1) {
37 const byte = ptr.*[i];
38
39 if (shift > T.bit_count)
40 return error.Overflow;
4131
42 var operand: T = undefined;32 return @truncate(T, value);
43 if (@shlWithOverflow(T, byte & 0x7f, @intCast(ShiftT, shift), &operand))33}
44 return error.Overflow;
4534
46 result |= operand;35/// Write a single unsigned integer as unsigned LEB128 to the given writer.
36pub fn writeULEB128(writer: anytype, uint_value: anytype) !void {
37 const T = @TypeOf(uint_value);
38 const U = if (T.bit_count < 8) u8 else T;
39 var value = @intCast(U, uint_value);
4740
48 if ((byte & 0x80) == 0) {41 while (true) {
49 ptr.* += i + 1;42 const byte = @truncate(u8, value & 0x7f);
50 return result;43 value >>= 7;
44 if (value == 0) {
45 try writer.writeByte(byte);
46 break;
47 } else {
48 try writer.writeByte(byte | 0x80);
51 }49 }
52
53 shift += 7;
54 }50 }
55}51}
5652
57pub fn readILEB128(comptime T: type, in_stream: var) !T {53/// Read a single unsinged integer from the given memory as type T.
58 const UT = std.meta.Int(false, T.bit_count);54/// The provided slice reference will be updated to point to the byte after the last byte read.
59 const ShiftT = std.meta.Int(false, std.math.log2(T.bit_count));55pub fn readULEB128Mem(comptime T: type, ptr: *[]const u8) !T {
56 var buf = std.io.fixedBufferStream(ptr.*);
57 const value = try readULEB128(T, buf.reader());
58 ptr.*.ptr += buf.pos;
59 return value;
60}
6061
61 var result: UT = 0;62/// Write a single unsigned LEB128 integer to the given memory as unsigned LEB128,
62 var shift: usize = 0;63/// returning the number of bytes written.
64pub fn writeULEB128Mem(ptr: []u8, uint_value: anytype) !usize {
65 const T = @TypeOf(uint_value);
66 const max_group = (T.bit_count + 6) / 7;
67 var buf = std.io.fixedBufferStream(ptr);
68 try writeULEB128(buf.writer(), uint_value);
69 return buf.pos;
70}
6371
64 while (true) {72/// Read a single signed LEB128 value from the given reader as type T,
65 const byte: u8 = try in_stream.readByte();73/// or error.Overflow if the value cannot fit.
74pub fn readILEB128(comptime T: type, reader: anytype) !T {
75 const S = if (T.bit_count < 8) i8 else T;
76 const U = std.meta.Int(false, S.bit_count);
77 const ShiftU = std.math.Log2Int(U);
6678
67 if (shift > T.bit_count)79 const max_group = (U.bit_count + 6) / 7;
68 return error.Overflow;
6980
70 var operand: UT = undefined;81 var value = @as(U, 0);
71 if (@shlWithOverflow(UT, @as(UT, byte & 0x7f), @intCast(ShiftT, shift), &operand)) {82 var group = @as(ShiftU, 0);
72 if (byte != 0x7f)
73 return error.Overflow;
74 }
7583
76 result |= operand;84 while (group < max_group) : (group += 1) {
85 const byte = try reader.readByte();
86 var temp = @as(U, byte & 0x7f);
7787
78 shift += 7;88 const shift = group * 7;
89 if (@shlWithOverflow(U, temp, shift, &temp)) {
90 // Overflow is ok so long as the sign bit is set and this is the last byte
91 if (byte & 0x80 != 0) return error.Overflow;
92 if (@bitCast(S, temp) >= 0) return error.Overflow;
7993
80 if ((byte & 0x80) == 0) {94 // and all the overflowed bits are 1
81 if (shift < T.bit_count and (byte & 0x40) != 0) {95 const remaining_shift = @intCast(u3, U.bit_count - @as(u16, shift));
82 result |= @bitCast(UT, @intCast(T, -1)) << @intCast(ShiftT, shift);96 const remaining_bits = @bitCast(i8, byte | 0x80) >> remaining_shift;
97 if (remaining_bits != -1) return error.Overflow;
98 }
99
100 value |= temp;
101 if (byte & 0x80 == 0) {
102 const needs_sign_ext = group + 1 < max_group;
103 if (byte & 0x40 != 0 and needs_sign_ext) {
104 const ones = @as(S, -1);
105 value |= @bitCast(U, ones) << (shift + 7);
83 }106 }
84 return @bitCast(T, result);107 break;
85 }108 }
109 } else {
110 return error.Overflow;
86 }111 }
87}
88112
89pub fn readILEB128Mem(comptime T: type, ptr: *[*]const u8) !T {113 const result = @bitCast(S, value);
90 const UT = std.meta.Int(false, T.bit_count);114 // Only applies if we extended to i8
91 const ShiftT = std.meta.Int(false, std.math.log2(T.bit_count));115 if (S != T) {
116 if (result > std.math.maxInt(T) or result < std.math.minInt(T)) return error.Overflow;
117 }
92118
93 var result: UT = 0;119 return @truncate(T, result);
94 var shift: usize = 0;120}
95 var i: usize = 0;
96121
97 while (true) : (i += 1) {122/// Write a single signed integer as signed LEB128 to the given writer.
98 const byte = ptr.*[i];123pub fn writeILEB128(writer: anytype, int_value: anytype) !void {
124 const T = @TypeOf(int_value);
125 const S = if (T.bit_count < 8) i8 else T;
126 const U = std.meta.Int(false, S.bit_count);
99127
100 if (shift > T.bit_count)128 var value = @intCast(S, int_value);
101 return error.Overflow;
102129
103 var operand: UT = undefined;130 while (true) {
104 if (@shlWithOverflow(UT, @as(UT, byte & 0x7f), @intCast(ShiftT, shift), &operand)) {131 const uvalue = @bitCast(U, value);
105 if (byte != 0x7f)132 const byte = @truncate(u8, uvalue);
106 return error.Overflow;133 value >>= 6;
134 if (value == -1 or value == 0) {
135 try writer.writeByte(byte & 0x7F);
136 break;
137 } else {
138 value >>= 1;
139 try writer.writeByte(byte | 0x80);
107 }140 }
141 }
142}
108143
109 result |= operand;144/// Read a single singed LEB128 integer from the given memory as type T.
110145/// The provided slice reference will be updated to point to the byte after the last byte read.
111 shift += 7;146pub fn readILEB128Mem(comptime T: type, ptr: *[]const u8) !T {
147 var buf = std.io.fixedBufferStream(ptr.*);
148 const value = try readILEB128(T, buf.reader());
149 ptr.*.ptr += buf.pos;
150 return value;
151}
112152
113 if ((byte & 0x80) == 0) {153/// Write a single signed LEB128 integer to the given memory as unsigned LEB128,
114 if (shift < T.bit_count and (byte & 0x40) != 0) {154/// returning the number of bytes written.
115 result |= @bitCast(UT, @intCast(T, -1)) << @intCast(ShiftT, shift);155pub fn writeILEB128Mem(ptr: []u8, int_value: anytype) !usize {
116 }156 const T = @TypeOf(int_value);
117 ptr.* += i + 1;157 var buf = std.io.fixedBufferStream(ptr);
118 return @bitCast(T, result);158 try writeILEB128(buf.writer(), int_value);
119 }159 return buf.pos;
120 }
121}160}
122161
162// tests
123fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {163fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
124 var in_stream = std.io.fixedBufferStream(encoded);164 var reader = std.io.fixedBufferStream(encoded);
125 return try readILEB128(T, in_stream.inStream());165 return try readILEB128(T, reader.reader());
126}166}
127167
128fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T {168fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T {
129 var in_stream = std.io.fixedBufferStream(encoded);169 var reader = std.io.fixedBufferStream(encoded);
130 return try readULEB128(T, in_stream.inStream());170 return try readULEB128(T, reader.reader());
131}171}
132172
133fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {173fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
134 var in_stream = std.io.fixedBufferStream(encoded);174 var reader = std.io.fixedBufferStream(encoded);
135 const v1 = readILEB128(T, in_stream.inStream());175 const v1 = try readILEB128(T, reader.reader());
136 var in_ptr = encoded.ptr;176 var in_ptr = encoded;
137 const v2 = readILEB128Mem(T, &in_ptr);177 const v2 = try readILEB128Mem(T, &in_ptr);
138 testing.expectEqual(v1, v2);178 testing.expectEqual(v1, v2);
139 return v1;179 return v1;
140}180}
141181
142fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {182fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
143 var in_stream = std.io.fixedBufferStream(encoded);183 var reader = std.io.fixedBufferStream(encoded);
144 const v1 = readULEB128(T, in_stream.inStream());184 const v1 = try readULEB128(T, reader.reader());
145 var in_ptr = encoded.ptr;185 var in_ptr = encoded;
146 const v2 = readULEB128Mem(T, &in_ptr);186 const v2 = try readULEB128Mem(T, &in_ptr);
147 testing.expectEqual(v1, v2);187 testing.expectEqual(v1, v2);
148 return v1;188 return v1;
149}189}
150190
151fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void {191fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) !void {
152 var in_stream = std.io.fixedBufferStream(encoded);192 var reader = std.io.fixedBufferStream(encoded);
153 var in_ptr = encoded.ptr;193 var in_ptr = encoded;
154 var i: usize = 0;194 var i: usize = 0;
155 while (i < N) : (i += 1) {195 while (i < N) : (i += 1) {
156 const v1 = readILEB128(T, in_stream.inStream());196 const v1 = try readILEB128(T, reader.reader());
157 const v2 = readILEB128Mem(T, &in_ptr);197 const v2 = try readILEB128Mem(T, &in_ptr);
158 testing.expectEqual(v1, v2);198 testing.expectEqual(v1, v2);
159 }199 }
160}200}
161201
162fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void {202fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) !void {
163 var in_stream = std.io.fixedBufferStream(encoded);203 var reader = std.io.fixedBufferStream(encoded);
164 var in_ptr = encoded.ptr;204 var in_ptr = encoded;
165 var i: usize = 0;205 var i: usize = 0;
166 while (i < N) : (i += 1) {206 while (i < N) : (i += 1) {
167 const v1 = readULEB128(T, in_stream.inStream());207 const v1 = try readULEB128(T, reader.reader());
168 const v2 = readULEB128Mem(T, &in_ptr);208 const v2 = try readULEB128Mem(T, &in_ptr);
169 testing.expectEqual(v1, v2);209 testing.expectEqual(v1, v2);
170 }210 }
171}211}
...@@ -212,7 +252,7 @@ test "deserialize signed LEB128" {...@@ -212,7 +252,7 @@ test "deserialize signed LEB128" {
212 testing.expect((try test_read_ileb128(i64, "\x80\x81\x80\x00")) == 0x80);252 testing.expect((try test_read_ileb128(i64, "\x80\x81\x80\x00")) == 0x80);
213253
214 // Decode sequence of SLEB128 values254 // Decode sequence of SLEB128 values
215 test_read_ileb128_seq(i64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00");255 try test_read_ileb128_seq(i64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00");
216}256}
217257
218test "deserialize unsigned LEB128" {258test "deserialize unsigned LEB128" {
...@@ -252,5 +292,99 @@ test "deserialize unsigned LEB128" {...@@ -252,5 +292,99 @@ test "deserialize unsigned LEB128" {
252 testing.expect((try test_read_uleb128(u64, "\x80\x81\x80\x00")) == 0x80);292 testing.expect((try test_read_uleb128(u64, "\x80\x81\x80\x00")) == 0x80);
253293
254 // Decode sequence of ULEB128 values294 // Decode sequence of ULEB128 values
255 test_read_uleb128_seq(u64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00");295 try test_read_uleb128_seq(u64, 4, "\x81\x01\x3f\x80\x7f\x80\x80\x80\x00");
296}
297
298fn test_write_leb128(value: anytype) !void {
299 const T = @TypeOf(value);
300
301 const writeStream = if (T.is_signed) writeILEB128 else writeULEB128;
302 const writeMem = if (T.is_signed) writeILEB128Mem else writeULEB128Mem;
303 const readStream = if (T.is_signed) readILEB128 else readULEB128;
304 const readMem = if (T.is_signed) readILEB128Mem else readULEB128Mem;
305
306 // decode to a larger bit size too, to ensure sign extension
307 // is working as expected
308 const larger_type_bits = ((T.bit_count + 8) / 8) * 8;
309 const B = std.meta.Int(T.is_signed, larger_type_bits);
310
311 const bytes_needed = bn: {
312 const S = std.meta.Int(T.is_signed, @sizeOf(T) * 8);
313 if (T.bit_count <= 7) break :bn @as(u16, 1);
314
315 const unused_bits = if (value < 0) @clz(T, ~value) else @clz(T, value);
316 const used_bits: u16 = (T.bit_count - unused_bits) + @boolToInt(T.is_signed);
317 if (used_bits <= 7) break :bn @as(u16, 1);
318 break :bn ((used_bits + 6) / 7);
319 };
320
321 const max_groups = if (T.bit_count == 0) 1 else (T.bit_count + 6) / 7;
322
323 var buf: [max_groups]u8 = undefined;
324 var fbs = std.io.fixedBufferStream(&buf);
325
326 // stream write
327 try writeStream(fbs.writer(), value);
328 const w1_pos = fbs.pos;
329 testing.expect(w1_pos == bytes_needed);
330
331 // stream read
332 fbs.pos = 0;
333 const sr = try readStream(T, fbs.reader());
334 testing.expect(fbs.pos == w1_pos);
335 testing.expect(sr == value);
336
337 // bigger type stream read
338 fbs.pos = 0;
339 const bsr = try readStream(B, fbs.reader());
340 testing.expect(fbs.pos == w1_pos);
341 testing.expect(bsr == value);
342
343 // mem write
344 const w2_pos = try writeMem(&buf, value);
345 testing.expect(w2_pos == w1_pos);
346
347 // mem read
348 var buf_ref: []u8 = buf[0..];
349 const mr = try readMem(T, &buf_ref);
350 testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos);
351 testing.expect(mr == value);
352
353 // bigger type mem read
354 buf_ref = buf[0..];
355 const bmr = try readMem(T, &buf_ref);
356 testing.expect(@ptrToInt(buf_ref.ptr) - @ptrToInt(&buf) == w2_pos);
357 testing.expect(bmr == value);
358}
359
360test "serialize unsigned LEB128" {
361 const max_bits = 18;
362
363 comptime var t = 0;
364 inline while (t <= max_bits) : (t += 1) {
365 const T = std.meta.Int(false, t);
366 const min = std.math.minInt(T);
367 const max = std.math.maxInt(T);
368 var i = @as(std.meta.Int(false, T.bit_count + 1), min);
369
370 while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i));
371 }
372}
373
374test "serialize signed LEB128" {
375 // explicitly test i0 because starting `t` at 0
376 // will break the while loop
377 try test_write_leb128(@as(i0, 0));
378
379 const max_bits = 18;
380
381 comptime var t = 1;
382 inline while (t <= max_bits) : (t += 1) {
383 const T = std.meta.Int(true, t);
384 const min = std.math.minInt(T);
385 const max = std.math.maxInt(T);
386 var i = @as(std.meta.Int(true, T.bit_count + 1), min);
387
388 while (i <= max) : (i += 1) try test_write_leb128(@intCast(T, i));
389 }
256}390}
lib/std/dwarf.zig+10-10
...@@ -236,7 +236,7 @@ const LineNumberProgram = struct {...@@ -236,7 +236,7 @@ const LineNumberProgram = struct {
236 }236 }
237};237};
238238
239fn readUnitLength(in_stream: var, endian: builtin.Endian, is_64: *bool) !u64 {239fn readUnitLength(in_stream: anytype, endian: builtin.Endian, is_64: *bool) !u64 {
240 const first_32_bits = try in_stream.readInt(u32, endian);240 const first_32_bits = try in_stream.readInt(u32, endian);
241 is_64.* = (first_32_bits == 0xffffffff);241 is_64.* = (first_32_bits == 0xffffffff);
242 if (is_64.*) {242 if (is_64.*) {
...@@ -249,7 +249,7 @@ fn readUnitLength(in_stream: var, endian: builtin.Endian, is_64: *bool) !u64 {...@@ -249,7 +249,7 @@ fn readUnitLength(in_stream: var, endian: builtin.Endian, is_64: *bool) !u64 {
249}249}
250250
251// TODO the nosuspends here are workarounds251// TODO the nosuspends here are workarounds
252fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {252fn readAllocBytes(allocator: *mem.Allocator, in_stream: anytype, size: usize) ![]u8 {
253 const buf = try allocator.alloc(u8, size);253 const buf = try allocator.alloc(u8, size);
254 errdefer allocator.free(buf);254 errdefer allocator.free(buf);
255 if ((try nosuspend in_stream.read(buf)) < size) return error.EndOfFile;255 if ((try nosuspend in_stream.read(buf)) < size) return error.EndOfFile;
...@@ -257,25 +257,25 @@ fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8...@@ -257,25 +257,25 @@ fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8
257}257}
258258
259// TODO the nosuspends here are workarounds259// TODO the nosuspends here are workarounds
260fn readAddress(in_stream: var, endian: builtin.Endian, is_64: bool) !u64 {260fn readAddress(in_stream: anytype, endian: builtin.Endian, is_64: bool) !u64 {
261 return nosuspend if (is_64)261 return nosuspend if (is_64)
262 try in_stream.readInt(u64, endian)262 try in_stream.readInt(u64, endian)
263 else263 else
264 @as(u64, try in_stream.readInt(u32, endian));264 @as(u64, try in_stream.readInt(u32, endian));
265}265}
266266
267fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {267fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: anytype, size: usize) !FormValue {
268 const buf = try readAllocBytes(allocator, in_stream, size);268 const buf = try readAllocBytes(allocator, in_stream, size);
269 return FormValue{ .Block = buf };269 return FormValue{ .Block = buf };
270}270}
271271
272// TODO the nosuspends here are workarounds272// TODO the nosuspends here are workarounds
273fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, endian: builtin.Endian, size: usize) !FormValue {273fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: anytype, endian: builtin.Endian, size: usize) !FormValue {
274 const block_len = try nosuspend in_stream.readVarInt(usize, endian, size);274 const block_len = try nosuspend in_stream.readVarInt(usize, endian, size);
275 return parseFormValueBlockLen(allocator, in_stream, block_len);275 return parseFormValueBlockLen(allocator, in_stream, block_len);
276}276}
277277
278fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, endian: builtin.Endian, comptime size: i32) !FormValue {278fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: anytype, signed: bool, endian: builtin.Endian, comptime size: i32) !FormValue {
279 // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here.279 // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here.
280 // `nosuspend` should be removed from all the function calls once it is fixed.280 // `nosuspend` should be removed from all the function calls once it is fixed.
281 return FormValue{281 return FormValue{
...@@ -302,7 +302,7 @@ fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: boo...@@ -302,7 +302,7 @@ fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: boo
302}302}
303303
304// TODO the nosuspends here are workarounds304// TODO the nosuspends here are workarounds
305fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, endian: builtin.Endian, size: i32) !FormValue {305fn parseFormValueRef(allocator: *mem.Allocator, in_stream: anytype, endian: builtin.Endian, size: i32) !FormValue {
306 return FormValue{306 return FormValue{
307 .Ref = switch (size) {307 .Ref = switch (size) {
308 1 => try nosuspend in_stream.readInt(u8, endian),308 1 => try nosuspend in_stream.readInt(u8, endian),
...@@ -316,7 +316,7 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, endian: builtin....@@ -316,7 +316,7 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, endian: builtin.
316}316}
317317
318// TODO the nosuspends here are workarounds318// TODO the nosuspends here are workarounds
319fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, endian: builtin.Endian, is_64: bool) anyerror!FormValue {319fn parseFormValue(allocator: *mem.Allocator, in_stream: anytype, form_id: u64, endian: builtin.Endian, is_64: bool) anyerror!FormValue {
320 return switch (form_id) {320 return switch (form_id) {
321 FORM_addr => FormValue{ .Address = try readAddress(in_stream, endian, @sizeOf(usize) == 8) },321 FORM_addr => FormValue{ .Address = try readAddress(in_stream, endian, @sizeOf(usize) == 8) },
322 FORM_block1 => parseFormValueBlock(allocator, in_stream, endian, 1),322 FORM_block1 => parseFormValueBlock(allocator, in_stream, endian, 1),
...@@ -359,7 +359,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, endia...@@ -359,7 +359,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, endia
359 const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, endian, is_64));359 const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, endian, is_64));
360 var frame = try allocator.create(F);360 var frame = try allocator.create(F);
361 defer allocator.destroy(frame);361 defer allocator.destroy(frame);
362 return await @asyncCall(frame, {}, parseFormValue, allocator, in_stream, child_form_id, endian, is_64);362 return await @asyncCall(frame, {}, parseFormValue, .{ allocator, in_stream, child_form_id, endian, is_64 });
363 },363 },
364 else => error.InvalidDebugInfo,364 else => error.InvalidDebugInfo,
365 };365 };
...@@ -670,7 +670,7 @@ pub const DwarfInfo = struct {...@@ -670,7 +670,7 @@ pub const DwarfInfo = struct {
670 }670 }
671 }671 }
672672
673 fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {673 fn parseDie(di: *DwarfInfo, in_stream: anytype, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
674 const abbrev_code = try leb.readULEB128(u64, in_stream);674 const abbrev_code = try leb.readULEB128(u64, in_stream);
675 if (abbrev_code == 0) return null;675 if (abbrev_code == 0) return null;
676 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;676 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
lib/std/elf.zig+3-2
...@@ -517,7 +517,7 @@ pub fn readAllHeaders(allocator: *mem.Allocator, file: File) !AllHeaders {...@@ -517,7 +517,7 @@ pub fn readAllHeaders(allocator: *mem.Allocator, file: File) !AllHeaders {
517 return hdrs;517 return hdrs;
518}518}
519519
520pub fn int(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {520pub fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
521 if (is_64) {521 if (is_64) {
522 if (need_bswap) {522 if (need_bswap) {
523 return @byteSwap(@TypeOf(int_64), int_64);523 return @byteSwap(@TypeOf(int_64), int_64);
...@@ -529,7 +529,7 @@ pub fn int(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_...@@ -529,7 +529,7 @@ pub fn int(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_
529 }529 }
530}530}
531531
532pub fn int32(need_bswap: bool, int_32: var, comptime Int64: var) Int64 {532pub fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 {
533 if (need_bswap) {533 if (need_bswap) {
534 return @byteSwap(@TypeOf(int_32), int_32);534 return @byteSwap(@TypeOf(int_32), int_32);
535 } else {535 } else {
...@@ -551,6 +551,7 @@ fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void {...@@ -551,6 +551,7 @@ fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void {
551 error.InputOutput => return error.FileSystem,551 error.InputOutput => return error.FileSystem,
552 error.Unexpected => return error.Unexpected,552 error.Unexpected => return error.Unexpected,
553 error.WouldBlock => return error.Unexpected,553 error.WouldBlock => return error.Unexpected,
554 error.AccessDenied => return error.Unexpected,
554 };555 };
555 if (len == 0) return error.UnexpectedEndOfFile;556 if (len == 0) return error.UnexpectedEndOfFile;
556 i += len;557 i += len;
lib/std/event/group.zig+1-1
...@@ -65,7 +65,7 @@ pub fn Group(comptime ReturnType: type) type {...@@ -65,7 +65,7 @@ pub fn Group(comptime ReturnType: type) type {
65 /// allocated by the group and freed by `wait`.65 /// allocated by the group and freed by `wait`.
66 /// `func` must be async and have return type `ReturnType`.66 /// `func` must be async and have return type `ReturnType`.
67 /// Thread-safe.67 /// Thread-safe.
68 pub fn call(self: *Self, comptime func: var, args: var) error{OutOfMemory}!void {68 pub fn call(self: *Self, comptime func: anytype, args: anytype) error{OutOfMemory}!void {
69 var frame = try self.allocator.create(@TypeOf(@call(.{ .modifier = .async_kw }, func, args)));69 var frame = try self.allocator.create(@TypeOf(@call(.{ .modifier = .async_kw }, func, args)));
70 errdefer self.allocator.destroy(frame);70 errdefer self.allocator.destroy(frame);
71 const node = try self.allocator.create(AllocStack.Node);71 const node = try self.allocator.create(AllocStack.Node);
lib/std/fmt.zig+266-219
...@@ -64,21 +64,22 @@ fn peekIsAlign(comptime fmt: []const u8) bool {...@@ -64,21 +64,22 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
64/// - `e`: output floating point value in scientific notation64/// - `e`: output floating point value in scientific notation
65/// - `d`: output numeric value in decimal notation65/// - `d`: output numeric value in decimal notation
66/// - `b`: output integer value in binary notation66/// - `b`: output integer value in binary notation
67/// - `o`: output integer value in octal notation
67/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max.68/// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max.
68/// - `*`: output the address of the value instead of the value itself.69/// - `*`: output the address of the value instead of the value itself.
69///70///
70/// If a formatted user type contains a function of the type71/// If a formatted user type contains a function of the type
71/// ```72/// ```
72/// pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: var) !void73/// pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void
73/// ```74/// ```
74/// with `?` being the type formatted, this function will be called instead of the default implementation.75/// with `?` being the type formatted, this function will be called instead of the default implementation.
75/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type.76/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type.
76///77///
77/// A user type may be a `struct`, `vector`, `union` or `enum` type.78/// A user type may be a `struct`, `vector`, `union` or `enum` type.
78pub fn format(79pub fn format(
79 out_stream: var,80 writer: anytype,
80 comptime fmt: []const u8,81 comptime fmt: []const u8,
81 args: var,82 args: anytype,
82) !void {83) !void {
83 const ArgSetType = u32;84 const ArgSetType = u32;
84 if (@typeInfo(@TypeOf(args)) != .Struct) {85 if (@typeInfo(@TypeOf(args)) != .Struct) {
...@@ -136,7 +137,7 @@ pub fn format(...@@ -136,7 +137,7 @@ pub fn format(
136 .Start => switch (c) {137 .Start => switch (c) {
137 '{' => {138 '{' => {
138 if (start_index < i) {139 if (start_index < i) {
139 try out_stream.writeAll(fmt[start_index..i]);140 try writer.writeAll(fmt[start_index..i]);
140 }141 }
141142
142 start_index = i;143 start_index = i;
...@@ -148,7 +149,7 @@ pub fn format(...@@ -148,7 +149,7 @@ pub fn format(
148 },149 },
149 '}' => {150 '}' => {
150 if (start_index < i) {151 if (start_index < i) {
151 try out_stream.writeAll(fmt[start_index..i]);152 try writer.writeAll(fmt[start_index..i]);
152 }153 }
153 state = .CloseBrace;154 state = .CloseBrace;
154 },155 },
...@@ -183,7 +184,7 @@ pub fn format(...@@ -183,7 +184,7 @@ pub fn format(
183 args[arg_to_print],184 args[arg_to_print],
184 fmt[0..0],185 fmt[0..0],
185 options,186 options,
186 out_stream,187 writer,
187 default_max_depth,188 default_max_depth,
188 );189 );
189190
...@@ -214,7 +215,7 @@ pub fn format(...@@ -214,7 +215,7 @@ pub fn format(
214 args[arg_to_print],215 args[arg_to_print],
215 fmt[specifier_start..i],216 fmt[specifier_start..i],
216 options,217 options,
217 out_stream,218 writer,
218 default_max_depth,219 default_max_depth,
219 );220 );
220 state = .Start;221 state = .Start;
...@@ -259,7 +260,7 @@ pub fn format(...@@ -259,7 +260,7 @@ pub fn format(
259 args[arg_to_print],260 args[arg_to_print],
260 fmt[specifier_start..specifier_end],261 fmt[specifier_start..specifier_end],
261 options,262 options,
262 out_stream,263 writer,
263 default_max_depth,264 default_max_depth,
264 );265 );
265 state = .Start;266 state = .Start;
...@@ -285,7 +286,7 @@ pub fn format(...@@ -285,7 +286,7 @@ pub fn format(
285 args[arg_to_print],286 args[arg_to_print],
286 fmt[specifier_start..specifier_end],287 fmt[specifier_start..specifier_end],
287 options,288 options,
288 out_stream,289 writer,
289 default_max_depth,290 default_max_depth,
290 );291 );
291 state = .Start;292 state = .Start;
...@@ -306,148 +307,149 @@ pub fn format(...@@ -306,148 +307,149 @@ pub fn format(
306 }307 }
307 }308 }
308 if (start_index < fmt.len) {309 if (start_index < fmt.len) {
309 try out_stream.writeAll(fmt[start_index..]);310 try writer.writeAll(fmt[start_index..]);
310 }311 }
311}312}
312313
313pub fn formatType(314pub fn formatType(
314 value: var,315 value: anytype,
315 comptime fmt: []const u8,316 comptime fmt: []const u8,
316 options: FormatOptions,317 options: FormatOptions,
317 out_stream: var,318 writer: anytype,
318 max_depth: usize,319 max_depth: usize,
319) @TypeOf(out_stream).Error!void {320) @TypeOf(writer).Error!void {
320 if (comptime std.mem.eql(u8, fmt, "*")) {321 if (comptime std.mem.eql(u8, fmt, "*")) {
321 try out_stream.writeAll(@typeName(@TypeOf(value).Child));322 try writer.writeAll(@typeName(@TypeOf(value).Child));
322 try out_stream.writeAll("@");323 try writer.writeAll("@");
323 try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, out_stream);324 try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
324 return;325 return;
325 }326 }
326327
327 const T = @TypeOf(value);328 const T = @TypeOf(value);
328 if (comptime std.meta.trait.hasFn("format")(T)) {329 if (comptime std.meta.trait.hasFn("format")(T)) {
329 return try value.format(fmt, options, out_stream);330 return try value.format(fmt, options, writer);
330 }331 }
331332
332 switch (@typeInfo(T)) {333 switch (@typeInfo(T)) {
333 .ComptimeInt, .Int, .ComptimeFloat, .Float => {334 .ComptimeInt, .Int, .ComptimeFloat, .Float => {
334 return formatValue(value, fmt, options, out_stream);335 return formatValue(value, fmt, options, writer);
335 },336 },
336 .Void => {337 .Void => {
337 return formatBuf("void", options, out_stream);338 return formatBuf("void", options, writer);
338 },339 },
339 .Bool => {340 .Bool => {
340 return formatBuf(if (value) "true" else "false", options, out_stream);341 return formatBuf(if (value) "true" else "false", options, writer);
341 },342 },
342 .Optional => {343 .Optional => {
343 if (value) |payload| {344 if (value) |payload| {
344 return formatType(payload, fmt, options, out_stream, max_depth);345 return formatType(payload, fmt, options, writer, max_depth);
345 } else {346 } else {
346 return formatBuf("null", options, out_stream);347 return formatBuf("null", options, writer);
347 }348 }
348 },349 },
349 .ErrorUnion => {350 .ErrorUnion => {
350 if (value) |payload| {351 if (value) |payload| {
351 return formatType(payload, fmt, options, out_stream, max_depth);352 return formatType(payload, fmt, options, writer, max_depth);
352 } else |err| {353 } else |err| {
353 return formatType(err, fmt, options, out_stream, max_depth);354 return formatType(err, fmt, options, writer, max_depth);
354 }355 }
355 },356 },
356 .ErrorSet => {357 .ErrorSet => {
357 try out_stream.writeAll("error.");358 try writer.writeAll("error.");
358 return out_stream.writeAll(@errorName(value));359 return writer.writeAll(@errorName(value));
359 },360 },
360 .Enum => |enumInfo| {361 .Enum => |enumInfo| {
361 try out_stream.writeAll(@typeName(T));362 try writer.writeAll(@typeName(T));
362 if (enumInfo.is_exhaustive) {363 if (enumInfo.is_exhaustive) {
363 try out_stream.writeAll(".");364 try writer.writeAll(".");
364 try out_stream.writeAll(@tagName(value));365 try writer.writeAll(@tagName(value));
365 return;366 return;
366 }367 }
367368
368 // Use @tagName only if value is one of known fields369 // Use @tagName only if value is one of known fields
370 @setEvalBranchQuota(3 * enumInfo.fields.len);
369 inline for (enumInfo.fields) |enumField| {371 inline for (enumInfo.fields) |enumField| {
370 if (@enumToInt(value) == enumField.value) {372 if (@enumToInt(value) == enumField.value) {
371 try out_stream.writeAll(".");373 try writer.writeAll(".");
372 try out_stream.writeAll(@tagName(value));374 try writer.writeAll(@tagName(value));
373 return;375 return;
374 }376 }
375 }377 }
376378
377 try out_stream.writeAll("(");379 try writer.writeAll("(");
378 try formatType(@enumToInt(value), fmt, options, out_stream, max_depth);380 try formatType(@enumToInt(value), fmt, options, writer, max_depth);
379 try out_stream.writeAll(")");381 try writer.writeAll(")");
380 },382 },
381 .Union => {383 .Union => {
382 try out_stream.writeAll(@typeName(T));384 try writer.writeAll(@typeName(T));
383 if (max_depth == 0) {385 if (max_depth == 0) {
384 return out_stream.writeAll("{ ... }");386 return writer.writeAll("{ ... }");
385 }387 }
386 const info = @typeInfo(T).Union;388 const info = @typeInfo(T).Union;
387 if (info.tag_type) |UnionTagType| {389 if (info.tag_type) |UnionTagType| {
388 try out_stream.writeAll("{ .");390 try writer.writeAll("{ .");
389 try out_stream.writeAll(@tagName(@as(UnionTagType, value)));391 try writer.writeAll(@tagName(@as(UnionTagType, value)));
390 try out_stream.writeAll(" = ");392 try writer.writeAll(" = ");
391 inline for (info.fields) |u_field| {393 inline for (info.fields) |u_field| {
392 if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) {394 if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) {
393 try formatType(@field(value, u_field.name), fmt, options, out_stream, max_depth - 1);395 try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1);
394 }396 }
395 }397 }
396 try out_stream.writeAll(" }");398 try writer.writeAll(" }");
397 } else {399 } else {
398 try format(out_stream, "@{x}", .{@ptrToInt(&value)});400 try format(writer, "@{x}", .{@ptrToInt(&value)});
399 }401 }
400 },402 },
401 .Struct => |StructT| {403 .Struct => |StructT| {
402 try out_stream.writeAll(@typeName(T));404 try writer.writeAll(@typeName(T));
403 if (max_depth == 0) {405 if (max_depth == 0) {
404 return out_stream.writeAll("{ ... }");406 return writer.writeAll("{ ... }");
405 }407 }
406 try out_stream.writeAll("{");408 try writer.writeAll("{");
407 inline for (StructT.fields) |f, i| {409 inline for (StructT.fields) |f, i| {
408 if (i == 0) {410 if (i == 0) {
409 try out_stream.writeAll(" .");411 try writer.writeAll(" .");
410 } else {412 } else {
411 try out_stream.writeAll(", .");413 try writer.writeAll(", .");
412 }414 }
413 try out_stream.writeAll(f.name);415 try writer.writeAll(f.name);
414 try out_stream.writeAll(" = ");416 try writer.writeAll(" = ");
415 try formatType(@field(value, f.name), fmt, options, out_stream, max_depth - 1);417 try formatType(@field(value, f.name), fmt, options, writer, max_depth - 1);
416 }418 }
417 try out_stream.writeAll(" }");419 try writer.writeAll(" }");
418 },420 },
419 .Pointer => |ptr_info| switch (ptr_info.size) {421 .Pointer => |ptr_info| switch (ptr_info.size) {
420 .One => switch (@typeInfo(ptr_info.child)) {422 .One => switch (@typeInfo(ptr_info.child)) {
421 .Array => |info| {423 .Array => |info| {
422 if (info.child == u8) {424 if (info.child == u8) {
423 return formatText(value, fmt, options, out_stream);425 return formatText(value, fmt, options, writer);
424 }426 }
425 return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });427 return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
426 },428 },
427 .Enum, .Union, .Struct => {429 .Enum, .Union, .Struct => {
428 return formatType(value.*, fmt, options, out_stream, max_depth);430 return formatType(value.*, fmt, options, writer, max_depth);
429 },431 },
430 else => return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),432 else => return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),
431 },433 },
432 .Many, .C => {434 .Many, .C => {
433 if (ptr_info.sentinel) |sentinel| {435 if (ptr_info.sentinel) |sentinel| {
434 return formatType(mem.span(value), fmt, options, out_stream, max_depth);436 return formatType(mem.span(value), fmt, options, writer, max_depth);
435 }437 }
436 if (ptr_info.child == u8) {438 if (ptr_info.child == u8) {
437 if (fmt.len > 0 and fmt[0] == 's') {439 if (fmt.len > 0 and fmt[0] == 's') {
438 return formatText(mem.span(value), fmt, options, out_stream);440 return formatText(mem.span(value), fmt, options, writer);
439 }441 }
440 }442 }
441 return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });443 return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
442 },444 },
443 .Slice => {445 .Slice => {
444 if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {446 if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
445 return formatText(value, fmt, options, out_stream);447 return formatText(value, fmt, options, writer);
446 }448 }
447 if (ptr_info.child == u8) {449 if (ptr_info.child == u8) {
448 return formatText(value, fmt, options, out_stream);450 return formatText(value, fmt, options, writer);
449 }451 }
450 return format(out_stream, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) });452 return format(writer, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) });
451 },453 },
452 },454 },
453 .Array => |info| {455 .Array => |info| {
...@@ -462,58 +464,58 @@ pub fn formatType(...@@ -462,58 +464,58 @@ pub fn formatType(
462 .sentinel = null,464 .sentinel = null,
463 },465 },
464 });466 });
465 return formatType(@as(Slice, &value), fmt, options, out_stream, max_depth);467 return formatType(@as(Slice, &value), fmt, options, writer, max_depth);
466 },468 },
467 .Vector => {469 .Vector => {
468 const len = @typeInfo(T).Vector.len;470 const len = @typeInfo(T).Vector.len;
469 try out_stream.writeAll("{ ");471 try writer.writeAll("{ ");
470 var i: usize = 0;472 var i: usize = 0;
471 while (i < len) : (i += 1) {473 while (i < len) : (i += 1) {
472 try formatValue(value[i], fmt, options, out_stream);474 try formatValue(value[i], fmt, options, writer);
473 if (i < len - 1) {475 if (i < len - 1) {
474 try out_stream.writeAll(", ");476 try writer.writeAll(", ");
475 }477 }
476 }478 }
477 try out_stream.writeAll(" }");479 try writer.writeAll(" }");
478 },480 },
479 .Fn => {481 .Fn => {
480 return format(out_stream, "{}@{x}", .{ @typeName(T), @ptrToInt(value) });482 return format(writer, "{}@{x}", .{ @typeName(T), @ptrToInt(value) });
481 },483 },
482 .Type => return out_stream.writeAll(@typeName(T)),484 .Type => return writer.writeAll(@typeName(T)),
483 .EnumLiteral => {485 .EnumLiteral => {
484 const buffer = [_]u8{'.'} ++ @tagName(value);486 const buffer = [_]u8{'.'} ++ @tagName(value);
485 return formatType(buffer, fmt, options, out_stream, max_depth);487 return formatType(buffer, fmt, options, writer, max_depth);
486 },488 },
487 else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),489 else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),
488 }490 }
489}491}
490492
491fn formatValue(493fn formatValue(
492 value: var,494 value: anytype,
493 comptime fmt: []const u8,495 comptime fmt: []const u8,
494 options: FormatOptions,496 options: FormatOptions,
495 out_stream: var,497 writer: anytype,
496) !void {498) !void {
497 if (comptime std.mem.eql(u8, fmt, "B")) {499 if (comptime std.mem.eql(u8, fmt, "B")) {
498 return formatBytes(value, options, 1000, out_stream);500 return formatBytes(value, options, 1000, writer);
499 } else if (comptime std.mem.eql(u8, fmt, "Bi")) {501 } else if (comptime std.mem.eql(u8, fmt, "Bi")) {
500 return formatBytes(value, options, 1024, out_stream);502 return formatBytes(value, options, 1024, writer);
501 }503 }
502504
503 const T = @TypeOf(value);505 const T = @TypeOf(value);
504 switch (@typeInfo(T)) {506 switch (@typeInfo(T)) {
505 .Float, .ComptimeFloat => return formatFloatValue(value, fmt, options, out_stream),507 .Float, .ComptimeFloat => return formatFloatValue(value, fmt, options, writer),
506 .Int, .ComptimeInt => return formatIntValue(value, fmt, options, out_stream),508 .Int, .ComptimeInt => return formatIntValue(value, fmt, options, writer),
507 .Bool => return formatBuf(if (value) "true" else "false", options, out_stream),509 .Bool => return formatBuf(if (value) "true" else "false", options, writer),
508 else => comptime unreachable,510 else => comptime unreachable,
509 }511 }
510}512}
511513
512pub fn formatIntValue(514pub fn formatIntValue(
513 value: var,515 value: anytype,
514 comptime fmt: []const u8,516 comptime fmt: []const u8,
515 options: FormatOptions,517 options: FormatOptions,
516 out_stream: var,518 writer: anytype,
517) !void {519) !void {
518 comptime var radix = 10;520 comptime var radix = 10;
519 comptime var uppercase = false;521 comptime var uppercase = false;
...@@ -529,7 +531,7 @@ pub fn formatIntValue(...@@ -529,7 +531,7 @@ pub fn formatIntValue(
529 uppercase = false;531 uppercase = false;
530 } else if (comptime std.mem.eql(u8, fmt, "c")) {532 } else if (comptime std.mem.eql(u8, fmt, "c")) {
531 if (@TypeOf(int_value).bit_count <= 8) {533 if (@TypeOf(int_value).bit_count <= 8) {
532 return formatAsciiChar(@as(u8, int_value), options, out_stream);534 return formatAsciiChar(@as(u8, int_value), options, writer);
533 } else {535 } else {
534 @compileError("Cannot print integer that is larger than 8 bits as a ascii");536 @compileError("Cannot print integer that is larger than 8 bits as a ascii");
535 }537 }
...@@ -542,23 +544,26 @@ pub fn formatIntValue(...@@ -542,23 +544,26 @@ pub fn formatIntValue(
542 } else if (comptime std.mem.eql(u8, fmt, "X")) {544 } else if (comptime std.mem.eql(u8, fmt, "X")) {
543 radix = 16;545 radix = 16;
544 uppercase = true;546 uppercase = true;
547 } else if (comptime std.mem.eql(u8, fmt, "o")) {
548 radix = 8;
549 uppercase = false;
545 } else {550 } else {
546 @compileError("Unknown format string: '" ++ fmt ++ "'");551 @compileError("Unknown format string: '" ++ fmt ++ "'");
547 }552 }
548553
549 return formatInt(int_value, radix, uppercase, options, out_stream);554 return formatInt(int_value, radix, uppercase, options, writer);
550}555}
551556
552fn formatFloatValue(557fn formatFloatValue(
553 value: var,558 value: anytype,
554 comptime fmt: []const u8,559 comptime fmt: []const u8,
555 options: FormatOptions,560 options: FormatOptions,
556 out_stream: var,561 writer: anytype,
557) !void {562) !void {
558 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {563 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {
559 return formatFloatScientific(value, options, out_stream);564 return formatFloatScientific(value, options, writer);
560 } else if (comptime std.mem.eql(u8, fmt, "d")) {565 } else if (comptime std.mem.eql(u8, fmt, "d")) {
561 return formatFloatDecimal(value, options, out_stream);566 return formatFloatDecimal(value, options, writer);
562 } else {567 } else {
563 @compileError("Unknown format string: '" ++ fmt ++ "'");568 @compileError("Unknown format string: '" ++ fmt ++ "'");
564 }569 }
...@@ -568,13 +573,13 @@ pub fn formatText(...@@ -568,13 +573,13 @@ pub fn formatText(
568 bytes: []const u8,573 bytes: []const u8,
569 comptime fmt: []const u8,574 comptime fmt: []const u8,
570 options: FormatOptions,575 options: FormatOptions,
571 out_stream: var,576 writer: anytype,
572) !void {577) !void {
573 if (comptime std.mem.eql(u8, fmt, "s") or (fmt.len == 0)) {578 if (comptime std.mem.eql(u8, fmt, "s") or (fmt.len == 0)) {
574 return formatBuf(bytes, options, out_stream);579 return formatBuf(bytes, options, writer);
575 } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {580 } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {
576 for (bytes) |c| {581 for (bytes) |c| {
577 try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, out_stream);582 try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, writer);
578 }583 }
579 return;584 return;
580 } else {585 } else {
...@@ -585,38 +590,38 @@ pub fn formatText(...@@ -585,38 +590,38 @@ pub fn formatText(
585pub fn formatAsciiChar(590pub fn formatAsciiChar(
586 c: u8,591 c: u8,
587 options: FormatOptions,592 options: FormatOptions,
588 out_stream: var,593 writer: anytype,
589) !void {594) !void {
590 return out_stream.writeAll(@as(*const [1]u8, &c));595 return writer.writeAll(@as(*const [1]u8, &c));
591}596}
592597
593pub fn formatBuf(598pub fn formatBuf(
594 buf: []const u8,599 buf: []const u8,
595 options: FormatOptions,600 options: FormatOptions,
596 out_stream: var,601 writer: anytype,
597) !void {602) !void {
598 const width = options.width orelse buf.len;603 const width = options.width orelse buf.len;
599 var padding = if (width > buf.len) (width - buf.len) else 0;604 var padding = if (width > buf.len) (width - buf.len) else 0;
600 const pad_byte = [1]u8{options.fill};605 const pad_byte = [1]u8{options.fill};
601 switch (options.alignment) {606 switch (options.alignment) {
602 .Left => {607 .Left => {
603 try out_stream.writeAll(buf);608 try writer.writeAll(buf);
604 while (padding > 0) : (padding -= 1) {609 while (padding > 0) : (padding -= 1) {
605 try out_stream.writeAll(&pad_byte);610 try writer.writeAll(&pad_byte);
606 }611 }
607 },612 },
608 .Center => {613 .Center => {
609 const padl = padding / 2;614 const padl = padding / 2;
610 var i: usize = 0;615 var i: usize = 0;
611 while (i < padl) : (i += 1) try out_stream.writeAll(&pad_byte);616 while (i < padl) : (i += 1) try writer.writeAll(&pad_byte);
612 try out_stream.writeAll(buf);617 try writer.writeAll(buf);
613 while (i < padding) : (i += 1) try out_stream.writeAll(&pad_byte);618 while (i < padding) : (i += 1) try writer.writeAll(&pad_byte);
614 },619 },
615 .Right => {620 .Right => {
616 while (padding > 0) : (padding -= 1) {621 while (padding > 0) : (padding -= 1) {
617 try out_stream.writeAll(&pad_byte);622 try writer.writeAll(&pad_byte);
618 }623 }
619 try out_stream.writeAll(buf);624 try writer.writeAll(buf);
620 },625 },
621 }626 }
622}627}
...@@ -625,40 +630,40 @@ pub fn formatBuf(...@@ -625,40 +630,40 @@ pub fn formatBuf(
625// It should be the case that every full precision, printed value can be re-parsed back to the630// It should be the case that every full precision, printed value can be re-parsed back to the
626// same type unambiguously.631// same type unambiguously.
627pub fn formatFloatScientific(632pub fn formatFloatScientific(
628 value: var,633 value: anytype,
629 options: FormatOptions,634 options: FormatOptions,
630 out_stream: var,635 writer: anytype,
631) !void {636) !void {
632 var x = @floatCast(f64, value);637 var x = @floatCast(f64, value);
633638
634 // Errol doesn't handle these special cases.639 // Errol doesn't handle these special cases.
635 if (math.signbit(x)) {640 if (math.signbit(x)) {
636 try out_stream.writeAll("-");641 try writer.writeAll("-");
637 x = -x;642 x = -x;
638 }643 }
639644
640 if (math.isNan(x)) {645 if (math.isNan(x)) {
641 return out_stream.writeAll("nan");646 return writer.writeAll("nan");
642 }647 }
643 if (math.isPositiveInf(x)) {648 if (math.isPositiveInf(x)) {
644 return out_stream.writeAll("inf");649 return writer.writeAll("inf");
645 }650 }
646 if (x == 0.0) {651 if (x == 0.0) {
647 try out_stream.writeAll("0");652 try writer.writeAll("0");
648653
649 if (options.precision) |precision| {654 if (options.precision) |precision| {
650 if (precision != 0) {655 if (precision != 0) {
651 try out_stream.writeAll(".");656 try writer.writeAll(".");
652 var i: usize = 0;657 var i: usize = 0;
653 while (i < precision) : (i += 1) {658 while (i < precision) : (i += 1) {
654 try out_stream.writeAll("0");659 try writer.writeAll("0");
655 }660 }
656 }661 }
657 } else {662 } else {
658 try out_stream.writeAll(".0");663 try writer.writeAll(".0");
659 }664 }
660665
661 try out_stream.writeAll("e+00");666 try writer.writeAll("e+00");
662 return;667 return;
663 }668 }
664669
...@@ -668,86 +673,86 @@ pub fn formatFloatScientific(...@@ -668,86 +673,86 @@ pub fn formatFloatScientific(
668 if (options.precision) |precision| {673 if (options.precision) |precision| {
669 errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);674 errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
670675
671 try out_stream.writeAll(float_decimal.digits[0..1]);676 try writer.writeAll(float_decimal.digits[0..1]);
672677
673 // {e0} case prints no `.`678 // {e0} case prints no `.`
674 if (precision != 0) {679 if (precision != 0) {
675 try out_stream.writeAll(".");680 try writer.writeAll(".");
676681
677 var printed: usize = 0;682 var printed: usize = 0;
678 if (float_decimal.digits.len > 1) {683 if (float_decimal.digits.len > 1) {
679 const num_digits = math.min(float_decimal.digits.len, precision + 1);684 const num_digits = math.min(float_decimal.digits.len, precision + 1);
680 try out_stream.writeAll(float_decimal.digits[1..num_digits]);685 try writer.writeAll(float_decimal.digits[1..num_digits]);
681 printed += num_digits - 1;686 printed += num_digits - 1;
682 }687 }
683688
684 while (printed < precision) : (printed += 1) {689 while (printed < precision) : (printed += 1) {
685 try out_stream.writeAll("0");690 try writer.writeAll("0");
686 }691 }
687 }692 }
688 } else {693 } else {
689 try out_stream.writeAll(float_decimal.digits[0..1]);694 try writer.writeAll(float_decimal.digits[0..1]);
690 try out_stream.writeAll(".");695 try writer.writeAll(".");
691 if (float_decimal.digits.len > 1) {696 if (float_decimal.digits.len > 1) {
692 const num_digits = if (@TypeOf(value) == f32) math.min(@as(usize, 9), float_decimal.digits.len) else float_decimal.digits.len;697 const num_digits = if (@TypeOf(value) == f32) math.min(@as(usize, 9), float_decimal.digits.len) else float_decimal.digits.len;
693698
694 try out_stream.writeAll(float_decimal.digits[1..num_digits]);699 try writer.writeAll(float_decimal.digits[1..num_digits]);
695 } else {700 } else {
696 try out_stream.writeAll("0");701 try writer.writeAll("0");
697 }702 }
698 }703 }
699704
700 try out_stream.writeAll("e");705 try writer.writeAll("e");
701 const exp = float_decimal.exp - 1;706 const exp = float_decimal.exp - 1;
702707
703 if (exp >= 0) {708 if (exp >= 0) {
704 try out_stream.writeAll("+");709 try writer.writeAll("+");
705 if (exp > -10 and exp < 10) {710 if (exp > -10 and exp < 10) {
706 try out_stream.writeAll("0");711 try writer.writeAll("0");
707 }712 }
708 try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, out_stream);713 try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, writer);
709 } else {714 } else {
710 try out_stream.writeAll("-");715 try writer.writeAll("-");
711 if (exp > -10 and exp < 10) {716 if (exp > -10 and exp < 10) {
712 try out_stream.writeAll("0");717 try writer.writeAll("0");
713 }718 }
714 try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, out_stream);719 try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, writer);
715 }720 }
716}721}
717722
718// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.723// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
719// By default floats are printed at full precision (no rounding).724// By default floats are printed at full precision (no rounding).
720pub fn formatFloatDecimal(725pub fn formatFloatDecimal(
721 value: var,726 value: anytype,
722 options: FormatOptions,727 options: FormatOptions,
723 out_stream: var,728 writer: anytype,
724) !void {729) !void {
725 var x = @as(f64, value);730 var x = @as(f64, value);
726731
727 // Errol doesn't handle these special cases.732 // Errol doesn't handle these special cases.
728 if (math.signbit(x)) {733 if (math.signbit(x)) {
729 try out_stream.writeAll("-");734 try writer.writeAll("-");
730 x = -x;735 x = -x;
731 }736 }
732737
733 if (math.isNan(x)) {738 if (math.isNan(x)) {
734 return out_stream.writeAll("nan");739 return writer.writeAll("nan");
735 }740 }
736 if (math.isPositiveInf(x)) {741 if (math.isPositiveInf(x)) {
737 return out_stream.writeAll("inf");742 return writer.writeAll("inf");
738 }743 }
739 if (x == 0.0) {744 if (x == 0.0) {
740 try out_stream.writeAll("0");745 try writer.writeAll("0");
741746
742 if (options.precision) |precision| {747 if (options.precision) |precision| {
743 if (precision != 0) {748 if (precision != 0) {
744 try out_stream.writeAll(".");749 try writer.writeAll(".");
745 var i: usize = 0;750 var i: usize = 0;
746 while (i < precision) : (i += 1) {751 while (i < precision) : (i += 1) {
747 try out_stream.writeAll("0");752 try writer.writeAll("0");
748 }753 }
749 } else {754 } else {
750 try out_stream.writeAll(".0");755 try writer.writeAll(".0");
751 }756 }
752 }757 }
753758
...@@ -769,14 +774,14 @@ pub fn formatFloatDecimal(...@@ -769,14 +774,14 @@ pub fn formatFloatDecimal(
769774
770 if (num_digits_whole > 0) {775 if (num_digits_whole > 0) {
771 // We may have to zero pad, for instance 1e4 requires zero padding.776 // We may have to zero pad, for instance 1e4 requires zero padding.
772 try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);777 try writer.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
773778
774 var i = num_digits_whole_no_pad;779 var i = num_digits_whole_no_pad;
775 while (i < num_digits_whole) : (i += 1) {780 while (i < num_digits_whole) : (i += 1) {
776 try out_stream.writeAll("0");781 try writer.writeAll("0");
777 }782 }
778 } else {783 } else {
779 try out_stream.writeAll("0");784 try writer.writeAll("0");
780 }785 }
781786
782 // {.0} special case doesn't want a trailing '.'787 // {.0} special case doesn't want a trailing '.'
...@@ -784,7 +789,7 @@ pub fn formatFloatDecimal(...@@ -784,7 +789,7 @@ pub fn formatFloatDecimal(
784 return;789 return;
785 }790 }
786791
787 try out_stream.writeAll(".");792 try writer.writeAll(".");
788793
789 // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's.794 // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's.
790 var printed: usize = 0;795 var printed: usize = 0;
...@@ -796,7 +801,7 @@ pub fn formatFloatDecimal(...@@ -796,7 +801,7 @@ pub fn formatFloatDecimal(
796801
797 var i: usize = 0;802 var i: usize = 0;
798 while (i < zeros_to_print) : (i += 1) {803 while (i < zeros_to_print) : (i += 1) {
799 try out_stream.writeAll("0");804 try writer.writeAll("0");
800 printed += 1;805 printed += 1;
801 }806 }
802807
...@@ -808,14 +813,14 @@ pub fn formatFloatDecimal(...@@ -808,14 +813,14 @@ pub fn formatFloatDecimal(
808 // Remaining fractional portion, zero-padding if insufficient.813 // Remaining fractional portion, zero-padding if insufficient.
809 assert(precision >= printed);814 assert(precision >= printed);
810 if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) {815 if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) {
811 try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);816 try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
812 return;817 return;
813 } else {818 } else {
814 try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);819 try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
815 printed += float_decimal.digits.len - num_digits_whole_no_pad;820 printed += float_decimal.digits.len - num_digits_whole_no_pad;
816821
817 while (printed < precision) : (printed += 1) {822 while (printed < precision) : (printed += 1) {
818 try out_stream.writeAll("0");823 try writer.writeAll("0");
819 }824 }
820 }825 }
821 } else {826 } else {
...@@ -827,14 +832,14 @@ pub fn formatFloatDecimal(...@@ -827,14 +832,14 @@ pub fn formatFloatDecimal(
827832
828 if (num_digits_whole > 0) {833 if (num_digits_whole > 0) {
829 // We may have to zero pad, for instance 1e4 requires zero padding.834 // We may have to zero pad, for instance 1e4 requires zero padding.
830 try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);835 try writer.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
831836
832 var i = num_digits_whole_no_pad;837 var i = num_digits_whole_no_pad;
833 while (i < num_digits_whole) : (i += 1) {838 while (i < num_digits_whole) : (i += 1) {
834 try out_stream.writeAll("0");839 try writer.writeAll("0");
835 }840 }
836 } else {841 } else {
837 try out_stream.writeAll("0");842 try writer.writeAll("0");
838 }843 }
839844
840 // Omit `.` if no fractional portion845 // Omit `.` if no fractional portion
...@@ -842,7 +847,7 @@ pub fn formatFloatDecimal(...@@ -842,7 +847,7 @@ pub fn formatFloatDecimal(
842 return;847 return;
843 }848 }
844849
845 try out_stream.writeAll(".");850 try writer.writeAll(".");
846851
847 // Zero-fill until we reach significant digits or run out of precision.852 // Zero-fill until we reach significant digits or run out of precision.
848 if (float_decimal.exp < 0) {853 if (float_decimal.exp < 0) {
...@@ -850,22 +855,22 @@ pub fn formatFloatDecimal(...@@ -850,22 +855,22 @@ pub fn formatFloatDecimal(
850855
851 var i: usize = 0;856 var i: usize = 0;
852 while (i < zero_digit_count) : (i += 1) {857 while (i < zero_digit_count) : (i += 1) {
853 try out_stream.writeAll("0");858 try writer.writeAll("0");
854 }859 }
855 }860 }
856861
857 try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);862 try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
858 }863 }
859}864}
860865
861pub fn formatBytes(866pub fn formatBytes(
862 value: var,867 value: anytype,
863 options: FormatOptions,868 options: FormatOptions,
864 comptime radix: usize,869 comptime radix: usize,
865 out_stream: var,870 writer: anytype,
866) !void {871) !void {
867 if (value == 0) {872 if (value == 0) {
868 return out_stream.writeAll("0B");873 return writer.writeAll("0B");
869 }874 }
870875
871 const is_float = comptime std.meta.trait.is(.Float)(@TypeOf(value));876 const is_float = comptime std.meta.trait.is(.Float)(@TypeOf(value));
...@@ -885,10 +890,10 @@ pub fn formatBytes(...@@ -885,10 +890,10 @@ pub fn formatBytes(
885 else => unreachable,890 else => unreachable,
886 };891 };
887892
888 try formatFloatDecimal(new_value, options, out_stream);893 try formatFloatDecimal(new_value, options, writer);
889894
890 if (suffix == ' ') {895 if (suffix == ' ') {
891 return out_stream.writeAll("B");896 return writer.writeAll("B");
892 }897 }
893898
894 const buf = switch (radix) {899 const buf = switch (radix) {
...@@ -896,15 +901,15 @@ pub fn formatBytes(...@@ -896,15 +901,15 @@ pub fn formatBytes(
896 1024 => &[_]u8{ suffix, 'i', 'B' },901 1024 => &[_]u8{ suffix, 'i', 'B' },
897 else => unreachable,902 else => unreachable,
898 };903 };
899 return out_stream.writeAll(buf);904 return writer.writeAll(buf);
900}905}
901906
902pub fn formatInt(907pub fn formatInt(
903 value: var,908 value: anytype,
904 base: u8,909 base: u8,
905 uppercase: bool,910 uppercase: bool,
906 options: FormatOptions,911 options: FormatOptions,
907 out_stream: var,912 writer: anytype,
908) !void {913) !void {
909 const int_value = if (@TypeOf(value) == comptime_int) blk: {914 const int_value = if (@TypeOf(value) == comptime_int) blk: {
910 const Int = math.IntFittingRange(value, value);915 const Int = math.IntFittingRange(value, value);
...@@ -913,18 +918,18 @@ pub fn formatInt(...@@ -913,18 +918,18 @@ pub fn formatInt(
913 value;918 value;
914919
915 if (@TypeOf(int_value).is_signed) {920 if (@TypeOf(int_value).is_signed) {
916 return formatIntSigned(int_value, base, uppercase, options, out_stream);921 return formatIntSigned(int_value, base, uppercase, options, writer);
917 } else {922 } else {
918 return formatIntUnsigned(int_value, base, uppercase, options, out_stream);923 return formatIntUnsigned(int_value, base, uppercase, options, writer);
919 }924 }
920}925}
921926
922fn formatIntSigned(927fn formatIntSigned(
923 value: var,928 value: anytype,
924 base: u8,929 base: u8,
925 uppercase: bool,930 uppercase: bool,
926 options: FormatOptions,931 options: FormatOptions,
927 out_stream: var,932 writer: anytype,
928) !void {933) !void {
929 const new_options = FormatOptions{934 const new_options = FormatOptions{
930 .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null,935 .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null,
...@@ -934,24 +939,24 @@ fn formatIntSigned(...@@ -934,24 +939,24 @@ fn formatIntSigned(
934 const bit_count = @typeInfo(@TypeOf(value)).Int.bits;939 const bit_count = @typeInfo(@TypeOf(value)).Int.bits;
935 const Uint = std.meta.Int(false, bit_count);940 const Uint = std.meta.Int(false, bit_count);
936 if (value < 0) {941 if (value < 0) {
937 try out_stream.writeAll("-");942 try writer.writeAll("-");
938 const new_value = math.absCast(value);943 const new_value = math.absCast(value);
939 return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream);944 return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
940 } else if (options.width == null or options.width.? == 0) {945 } else if (options.width == null or options.width.? == 0) {
941 return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, out_stream);946 return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, writer);
942 } else {947 } else {
943 try out_stream.writeAll("+");948 try writer.writeAll("+");
944 const new_value = @intCast(Uint, value);949 const new_value = @intCast(Uint, value);
945 return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream);950 return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
946 }951 }
947}952}
948953
949fn formatIntUnsigned(954fn formatIntUnsigned(
950 value: var,955 value: anytype,
951 base: u8,956 base: u8,
952 uppercase: bool,957 uppercase: bool,
953 options: FormatOptions,958 options: FormatOptions,
954 out_stream: var,959 writer: anytype,
955) !void {960) !void {
956 assert(base >= 2);961 assert(base >= 2);
957 var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined;962 var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined;
...@@ -976,68 +981,96 @@ fn formatIntUnsigned(...@@ -976,68 +981,96 @@ fn formatIntUnsigned(
976 const zero_byte: u8 = options.fill;981 const zero_byte: u8 = options.fill;
977 var leftover_padding = padding - index;982 var leftover_padding = padding - index;
978 while (true) {983 while (true) {
979 try out_stream.writeAll(@as(*const [1]u8, &zero_byte)[0..]);984 try writer.writeAll(@as(*const [1]u8, &zero_byte)[0..]);
980 leftover_padding -= 1;985 leftover_padding -= 1;
981 if (leftover_padding == 0) break;986 if (leftover_padding == 0) break;
982 }987 }
983 mem.set(u8, buf[0..index], options.fill);988 mem.set(u8, buf[0..index], options.fill);
984 return out_stream.writeAll(&buf);989 return writer.writeAll(&buf);
985 } else {990 } else {
986 const padded_buf = buf[index - padding ..];991 const padded_buf = buf[index - padding ..];
987 mem.set(u8, padded_buf[0..padding], options.fill);992 mem.set(u8, padded_buf[0..padding], options.fill);
988 return out_stream.writeAll(padded_buf);993 return writer.writeAll(padded_buf);
989 }994 }
990}995}
991996
992pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize {997pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, options: FormatOptions) usize {
993 var fbs = std.io.fixedBufferStream(out_buf);998 var fbs = std.io.fixedBufferStream(out_buf);
994 formatInt(value, base, uppercase, options, fbs.outStream()) catch unreachable;999 formatInt(value, base, uppercase, options, fbs.writer()) catch unreachable;
995 return fbs.pos;1000 return fbs.pos;
996}1001}
9971002
998pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T {1003pub const ParseIntError = error{
999 if (!T.is_signed) return parseUnsigned(T, buf, radix);
1000 if (buf.len == 0) return @as(T, 0);
1001 if (buf[0] == '-') {
1002 return math.negate(try parseUnsigned(T, buf[1..], radix));
1003 } else if (buf[0] == '+') {
1004 return parseUnsigned(T, buf[1..], radix);
1005 } else {
1006 return parseUnsigned(T, buf, radix);
1007 }
1008}
1009
1010test "parseInt" {
1011 std.testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10);
1012 std.testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10);
1013 std.testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter);
1014 std.testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter);
1015 std.testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter);
1016 std.testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255);
1017 std.testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow);
1018}
1019
1020pub const ParseUnsignedError = error{
1021 /// The result cannot fit in the type specified1004 /// The result cannot fit in the type specified
1022 Overflow,1005 Overflow,
10231006
1024 /// The input had a byte that was not a digit1007 /// The input was empty or had a byte that was not a digit
1025 InvalidCharacter,1008 InvalidCharacter,
1026};1009};
10271010
1028pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T {1011pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) ParseIntError!T {
1012 if (buf.len == 0) return error.InvalidCharacter;
1013 if (buf[0] == '+') return parseWithSign(T, buf[1..], radix, .Pos);
1014 if (buf[0] == '-') return parseWithSign(T, buf[1..], radix, .Neg);
1015 return parseWithSign(T, buf, radix, .Pos);
1016}
1017
1018test "parseInt" {
1019 std.testing.expect((try parseInt(i32, "-10", 10)) == -10);
1020 std.testing.expect((try parseInt(i32, "+10", 10)) == 10);
1021 std.testing.expect((try parseInt(u32, "+10", 10)) == 10);
1022 std.testing.expectError(error.Overflow, parseInt(u32, "-10", 10));
1023 std.testing.expectError(error.InvalidCharacter, parseInt(u32, " 10", 10));
1024 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "10 ", 10));
1025 std.testing.expect((try parseInt(u8, "255", 10)) == 255);
1026 std.testing.expectError(error.Overflow, parseInt(u8, "256", 10));
1027
1028 // +0 and -0 should work for unsigned
1029 std.testing.expect((try parseInt(u8, "-0", 10)) == 0);
1030 std.testing.expect((try parseInt(u8, "+0", 10)) == 0);
1031
1032 // ensure minInt is parsed correctly
1033 std.testing.expect((try parseInt(i8, "-128", 10)) == math.minInt(i8));
1034 std.testing.expect((try parseInt(i43, "-4398046511104", 10)) == math.minInt(i43));
1035
1036 // empty string or bare +- is invalid
1037 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "", 10));
1038 std.testing.expectError(error.InvalidCharacter, parseInt(i32, "", 10));
1039 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "+", 10));
1040 std.testing.expectError(error.InvalidCharacter, parseInt(i32, "+", 10));
1041 std.testing.expectError(error.InvalidCharacter, parseInt(u32, "-", 10));
1042 std.testing.expectError(error.InvalidCharacter, parseInt(i32, "-", 10));
1043}
1044
1045fn parseWithSign(
1046 comptime T: type,
1047 buf: []const u8,
1048 radix: u8,
1049 comptime sign: enum { Pos, Neg },
1050) ParseIntError!T {
1051 if (buf.len == 0) return error.InvalidCharacter;
1052
1053 const add = switch (sign) {
1054 .Pos => math.add,
1055 .Neg => math.sub,
1056 };
1057
1029 var x: T = 0;1058 var x: T = 0;
10301059
1031 for (buf) |c| {1060 for (buf) |c| {
1032 const digit = try charToDigit(c, radix);1061 const digit = try charToDigit(c, radix);
10331062
1034 if (x != 0) x = try math.mul(T, x, try math.cast(T, radix));1063 if (x != 0) x = try math.mul(T, x, try math.cast(T, radix));
1035 x = try math.add(T, x, try math.cast(T, digit));1064 x = try add(T, x, try math.cast(T, digit));
1036 }1065 }
10371066
1038 return x;1067 return x;
1039}1068}
10401069
1070pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseIntError!T {
1071 return parseWithSign(T, buf, radix, .Pos);
1072}
1073
1041test "parseUnsigned" {1074test "parseUnsigned" {
1042 std.testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124);1075 std.testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124);
1043 std.testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535);1076 std.testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535);
...@@ -1063,6 +1096,13 @@ test "parseUnsigned" {...@@ -1063,6 +1096,13 @@ test "parseUnsigned" {
1063 std.testing.expect((try parseUnsigned(u1, "001", 16)) == 1);1096 std.testing.expect((try parseUnsigned(u1, "001", 16)) == 1);
1064 std.testing.expect((try parseUnsigned(u2, "3", 16)) == 3);1097 std.testing.expect((try parseUnsigned(u2, "3", 16)) == 3);
1065 std.testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16));1098 std.testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16));
1099
1100 // parseUnsigned does not expect a sign
1101 std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "+0", 10));
1102 std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "-0", 10));
1103
1104 // test empty string error
1105 std.testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "", 10));
1066}1106}
10671107
1068pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;1108pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;
...@@ -1096,22 +1136,22 @@ pub const BufPrintError = error{...@@ -1096,22 +1136,22 @@ pub const BufPrintError = error{
1096 /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes.1136 /// As much as possible was written to the buffer, but it was too small to fit all the printed bytes.
1097 NoSpaceLeft,1137 NoSpaceLeft,
1098};1138};
1099pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 {1139pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![]u8 {
1100 var fbs = std.io.fixedBufferStream(buf);1140 var fbs = std.io.fixedBufferStream(buf);
1101 try format(fbs.outStream(), fmt, args);1141 try format(fbs.writer(), fmt, args);
1102 return fbs.getWritten();1142 return fbs.getWritten();
1103}1143}
11041144
1105// Count the characters needed for format. Useful for preallocating memory1145// Count the characters needed for format. Useful for preallocating memory
1106pub fn count(comptime fmt: []const u8, args: var) u64 {1146pub fn count(comptime fmt: []const u8, args: anytype) u64 {
1107 var counting_stream = std.io.countingOutStream(std.io.null_out_stream);1147 var counting_writer = std.io.countingWriter(std.io.null_writer);
1108 format(counting_stream.outStream(), fmt, args) catch |err| switch (err) {};1148 format(counting_writer.writer(), fmt, args) catch |err| switch (err) {};
1109 return counting_stream.bytes_written;1149 return counting_writer.bytes_written;
1110}1150}
11111151
1112pub const AllocPrintError = error{OutOfMemory};1152pub const AllocPrintError = error{OutOfMemory};
11131153
1114pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 {1154pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: anytype) AllocPrintError![]u8 {
1115 const size = math.cast(usize, count(fmt, args)) catch |err| switch (err) {1155 const size = math.cast(usize, count(fmt, args)) catch |err| switch (err) {
1116 // Output too long. Can't possibly allocate enough memory to display it.1156 // Output too long. Can't possibly allocate enough memory to display it.
1117 error.Overflow => return error.OutOfMemory,1157 error.Overflow => return error.OutOfMemory,
...@@ -1122,7 +1162,7 @@ pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var...@@ -1122,7 +1162,7 @@ pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var
1122 };1162 };
1123}1163}
11241164
1125pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 {1165pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: anytype) AllocPrintError![:0]u8 {
1126 const result = try allocPrint(allocator, fmt ++ "\x00", args);1166 const result = try allocPrint(allocator, fmt ++ "\x00", args);
1127 return result[0 .. result.len - 1 :0];1167 return result[0 .. result.len - 1 :0];
1128}1168}
...@@ -1148,7 +1188,7 @@ test "bufPrintInt" {...@@ -1148,7 +1188,7 @@ test "bufPrintInt" {
1148 std.testing.expectEqualSlices(u8, "-42", bufPrintIntToSlice(buf, @as(i32, -42), 10, false, FormatOptions{ .width = 3 }));1188 std.testing.expectEqualSlices(u8, "-42", bufPrintIntToSlice(buf, @as(i32, -42), 10, false, FormatOptions{ .width = 3 }));
1149}1189}
11501190
1151fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) []u8 {1191fn bufPrintIntToSlice(buf: []u8, value: anytype, base: u8, uppercase: bool, options: FormatOptions) []u8 {
1152 return buf[0..formatIntBuf(buf, value, base, uppercase, options)];1192 return buf[0..formatIntBuf(buf, value, base, uppercase, options)];
1153}1193}
11541194
...@@ -1204,6 +1244,10 @@ test "int.specifier" {...@@ -1204,6 +1244,10 @@ test "int.specifier" {
1204 const value: u8 = 0b1100;1244 const value: u8 = 0b1100;
1205 try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value});1245 try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value});
1206 }1246 }
1247 {
1248 const value: u16 = 0o1234;
1249 try testFmt("u16: 0o1234\n", "u16: 0o{o}\n", .{value});
1250 }
1207}1251}
12081252
1209test "int.padded" {1253test "int.padded" {
...@@ -1215,15 +1259,15 @@ test "buffer" {...@@ -1215,15 +1259,15 @@ test "buffer" {
1215 {1259 {
1216 var buf1: [32]u8 = undefined;1260 var buf1: [32]u8 = undefined;
1217 var fbs = std.io.fixedBufferStream(&buf1);1261 var fbs = std.io.fixedBufferStream(&buf1);
1218 try formatType(1234, "", FormatOptions{}, fbs.outStream(), default_max_depth);1262 try formatType(1234, "", FormatOptions{}, fbs.writer(), default_max_depth);
1219 std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234"));1263 std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234"));
12201264
1221 fbs.reset();1265 fbs.reset();
1222 try formatType('a', "c", FormatOptions{}, fbs.outStream(), default_max_depth);1266 try formatType('a', "c", FormatOptions{}, fbs.writer(), default_max_depth);
1223 std.testing.expect(mem.eql(u8, fbs.getWritten(), "a"));1267 std.testing.expect(mem.eql(u8, fbs.getWritten(), "a"));
12241268
1225 fbs.reset();1269 fbs.reset();
1226 try formatType(0b1100, "b", FormatOptions{}, fbs.outStream(), default_max_depth);1270 try formatType(0b1100, "b", FormatOptions{}, fbs.writer(), default_max_depth);
1227 std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100"));1271 std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100"));
1228 }1272 }
1229}1273}
...@@ -1321,6 +1365,9 @@ test "enum" {...@@ -1321,6 +1365,9 @@ test "enum" {
1321 try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value});1365 try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value});
1322 try testFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One});1366 try testFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One});
1323 try testFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two});1367 try testFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two});
1368
1369 // test very large enum to verify ct branch quota is large enough
1370 try testFmt("enum: Win32Error.INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION});
1324}1371}
13251372
1326test "non-exhaustive enum" {1373test "non-exhaustive enum" {
...@@ -1413,12 +1460,12 @@ test "custom" {...@@ -1413,12 +1460,12 @@ test "custom" {
1413 self: SelfType,1460 self: SelfType,
1414 comptime fmt: []const u8,1461 comptime fmt: []const u8,
1415 options: FormatOptions,1462 options: FormatOptions,
1416 out_stream: var,1463 writer: anytype,
1417 ) !void {1464 ) !void {
1418 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) {1465 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) {
1419 return std.fmt.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y });1466 return std.fmt.format(writer, "({d:.3},{d:.3})", .{ self.x, self.y });
1420 } else if (comptime std.mem.eql(u8, fmt, "d")) {1467 } else if (comptime std.mem.eql(u8, fmt, "d")) {
1421 return std.fmt.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y });1468 return std.fmt.format(writer, "{d:.3}x{d:.3}", .{ self.x, self.y });
1422 } else {1469 } else {
1423 @compileError("Unknown format character: '" ++ fmt ++ "'");1470 @compileError("Unknown format character: '" ++ fmt ++ "'");
1424 }1471 }
...@@ -1534,7 +1581,7 @@ test "bytes.hex" {...@@ -1534,7 +1581,7 @@ test "bytes.hex" {
1534 try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});1581 try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});
1535}1582}
15361583
1537fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void {1584fn testFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void {
1538 var buf: [100]u8 = undefined;1585 var buf: [100]u8 = undefined;
1539 const result = try bufPrint(buf[0..], template, args);1586 const result = try bufPrint(buf[0..], template, args);
1540 if (mem.eql(u8, result, expected)) return;1587 if (mem.eql(u8, result, expected)) return;
...@@ -1604,7 +1651,7 @@ test "formatIntValue with comptime_int" {...@@ -1604,7 +1651,7 @@ test "formatIntValue with comptime_int" {
16041651
1605 var buf: [20]u8 = undefined;1652 var buf: [20]u8 = undefined;
1606 var fbs = std.io.fixedBufferStream(&buf);1653 var fbs = std.io.fixedBufferStream(&buf);
1607 try formatIntValue(value, "", FormatOptions{}, fbs.outStream());1654 try formatIntValue(value, "", FormatOptions{}, fbs.writer());
1608 std.testing.expect(mem.eql(u8, fbs.getWritten(), "123456789123456789"));1655 std.testing.expect(mem.eql(u8, fbs.getWritten(), "123456789123456789"));
1609}1656}
16101657
...@@ -1613,7 +1660,7 @@ test "formatFloatValue with comptime_float" {...@@ -1613,7 +1660,7 @@ test "formatFloatValue with comptime_float" {
16131660
1614 var buf: [20]u8 = undefined;1661 var buf: [20]u8 = undefined;
1615 var fbs = std.io.fixedBufferStream(&buf);1662 var fbs = std.io.fixedBufferStream(&buf);
1616 try formatFloatValue(value, "", FormatOptions{}, fbs.outStream());1663 try formatFloatValue(value, "", FormatOptions{}, fbs.writer());
1617 std.testing.expect(mem.eql(u8, fbs.getWritten(), "1.0e+00"));1664 std.testing.expect(mem.eql(u8, fbs.getWritten(), "1.0e+00"));
16181665
1619 try testFmt("1.0e+00", "{}", .{value});1666 try testFmt("1.0e+00", "{}", .{value});
...@@ -1630,10 +1677,10 @@ test "formatType max_depth" {...@@ -1630,10 +1677,10 @@ test "formatType max_depth" {
1630 self: SelfType,1677 self: SelfType,
1631 comptime fmt: []const u8,1678 comptime fmt: []const u8,
1632 options: FormatOptions,1679 options: FormatOptions,
1633 out_stream: var,1680 writer: anytype,
1634 ) !void {1681 ) !void {
1635 if (fmt.len == 0) {1682 if (fmt.len == 0) {
1636 return std.fmt.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y });1683 return std.fmt.format(writer, "({d:.3},{d:.3})", .{ self.x, self.y });
1637 } else {1684 } else {
1638 @compileError("Unknown format string: '" ++ fmt ++ "'");1685 @compileError("Unknown format string: '" ++ fmt ++ "'");
1639 }1686 }
...@@ -1669,19 +1716,19 @@ test "formatType max_depth" {...@@ -1669,19 +1716,19 @@ test "formatType max_depth" {
16691716
1670 var buf: [1000]u8 = undefined;1717 var buf: [1000]u8 = undefined;
1671 var fbs = std.io.fixedBufferStream(&buf);1718 var fbs = std.io.fixedBufferStream(&buf);
1672 try formatType(inst, "", FormatOptions{}, fbs.outStream(), 0);1719 try formatType(inst, "", FormatOptions{}, fbs.writer(), 0);
1673 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ ... }"));1720 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ ... }"));
16741721
1675 fbs.reset();1722 fbs.reset();
1676 try formatType(inst, "", FormatOptions{}, fbs.outStream(), 1);1723 try formatType(inst, "", FormatOptions{}, fbs.writer(), 1);
1677 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }"));1724 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }"));
16781725
1679 fbs.reset();1726 fbs.reset();
1680 try formatType(inst, "", FormatOptions{}, fbs.outStream(), 2);1727 try formatType(inst, "", FormatOptions{}, fbs.writer(), 2);
1681 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }"));1728 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }"));
16821729
1683 fbs.reset();1730 fbs.reset();
1684 try formatType(inst, "", FormatOptions{}, fbs.outStream(), 3);1731 try formatType(inst, "", FormatOptions{}, fbs.writer(), 3);
1685 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }"));1732 std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }"));
1686}1733}
16871734
lib/std/fs.zig+55-36
...@@ -261,17 +261,7 @@ pub const Dir = struct {...@@ -261,17 +261,7 @@ pub const Dir = struct {
261 name: []const u8,261 name: []const u8,
262 kind: Kind,262 kind: Kind,
263263
264 pub const Kind = enum {264 pub const Kind = File.Kind;
265 BlockDevice,
266 CharacterDevice,
267 Directory,
268 NamedPipe,
269 SymLink,
270 File,
271 UnixDomainSocket,
272 Whiteout,
273 Unknown,
274 };
275 };265 };
276266
277 const IteratorError = error{AccessDenied} || os.UnexpectedError;267 const IteratorError = error{AccessDenied} || os.UnexpectedError;
...@@ -463,6 +453,8 @@ pub const Dir = struct {...@@ -463,6 +453,8 @@ pub const Dir = struct {
463453
464 pub const Error = IteratorError;454 pub const Error = IteratorError;
465455
456 /// Memory such as file names referenced in this returned entry becomes invalid
457 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
466 pub fn next(self: *Self) Error!?Entry {458 pub fn next(self: *Self) Error!?Entry {
467 start_over: while (true) {459 start_over: while (true) {
468 const w = os.windows;460 const w = os.windows;
...@@ -545,14 +537,15 @@ pub const Dir = struct {...@@ -545,14 +537,15 @@ pub const Dir = struct {
545 w.EFAULT => unreachable,537 w.EFAULT => unreachable,
546 w.ENOTDIR => unreachable,538 w.ENOTDIR => unreachable,
547 w.EINVAL => unreachable,539 w.EINVAL => unreachable,
540 w.ENOTCAPABLE => return error.AccessDenied,
548 else => |err| return os.unexpectedErrno(err),541 else => |err| return os.unexpectedErrno(err),
549 }542 }
550 if (bufused == 0) return null;543 if (bufused == 0) return null;
551 self.index = 0;544 self.index = 0;
552 self.end_index = bufused;545 self.end_index = bufused;
553 }546 }
554 const entry = @ptrCast(*align(1) os.wasi.dirent_t, &self.buf[self.index]);547 const entry = @ptrCast(*align(1) w.dirent_t, &self.buf[self.index]);
555 const entry_size = @sizeOf(os.wasi.dirent_t);548 const entry_size = @sizeOf(w.dirent_t);
556 const name_index = self.index + entry_size;549 const name_index = self.index + entry_size;
557 const name = mem.span(self.buf[name_index .. name_index + entry.d_namlen]);550 const name = mem.span(self.buf[name_index .. name_index + entry.d_namlen]);
558551
...@@ -566,12 +559,12 @@ pub const Dir = struct {...@@ -566,12 +559,12 @@ pub const Dir = struct {
566 }559 }
567560
568 const entry_kind = switch (entry.d_type) {561 const entry_kind = switch (entry.d_type) {
569 wasi.FILETYPE_BLOCK_DEVICE => Entry.Kind.BlockDevice,562 w.FILETYPE_BLOCK_DEVICE => Entry.Kind.BlockDevice,
570 wasi.FILETYPE_CHARACTER_DEVICE => Entry.Kind.CharacterDevice,563 w.FILETYPE_CHARACTER_DEVICE => Entry.Kind.CharacterDevice,
571 wasi.FILETYPE_DIRECTORY => Entry.Kind.Directory,564 w.FILETYPE_DIRECTORY => Entry.Kind.Directory,
572 wasi.FILETYPE_SYMBOLIC_LINK => Entry.Kind.SymLink,565 w.FILETYPE_SYMBOLIC_LINK => Entry.Kind.SymLink,
573 wasi.FILETYPE_REGULAR_FILE => Entry.Kind.File,566 w.FILETYPE_REGULAR_FILE => Entry.Kind.File,
574 wasi.FILETYPE_SOCKET_STREAM, wasi.FILETYPE_SOCKET_DGRAM => Entry.Kind.UnixDomainSocket,567 w.FILETYPE_SOCKET_STREAM, wasi.FILETYPE_SOCKET_DGRAM => Entry.Kind.UnixDomainSocket,
575 else => Entry.Kind.Unknown,568 else => Entry.Kind.Unknown,
576 };569 };
577 return Entry{570 return Entry{
...@@ -1109,6 +1102,7 @@ pub const Dir = struct {...@@ -1109,6 +1102,7 @@ pub const Dir = struct {
1109 .OBJECT_NAME_INVALID => unreachable,1102 .OBJECT_NAME_INVALID => unreachable,
1110 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,1103 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
1111 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,1104 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
1105 .NOT_A_DIRECTORY => return error.NotDir,
1112 .INVALID_PARAMETER => unreachable,1106 .INVALID_PARAMETER => unreachable,
1113 else => return w.unexpectedStatus(rc),1107 else => return w.unexpectedStatus(rc),
1114 }1108 }
...@@ -1119,10 +1113,18 @@ pub const Dir = struct {...@@ -1119,10 +1113,18 @@ pub const Dir = struct {
1119 /// Delete a file name and possibly the file it refers to, based on an open directory handle.1113 /// Delete a file name and possibly the file it refers to, based on an open directory handle.
1120 /// Asserts that the path parameter has no null bytes.1114 /// Asserts that the path parameter has no null bytes.
1121 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {1115 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
1122 os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) {1116 if (builtin.os.tag == .windows) {
1123 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR1117 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1124 else => |e| return e,1118 return self.deleteFileW(sub_path_w.span().ptr);
1125 };1119 } else if (builtin.os.tag == .wasi) {
1120 os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) {
1121 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
1122 else => |e| return e,
1123 };
1124 } else {
1125 const sub_path_c = try os.toPosixPath(sub_path);
1126 return self.deleteFileZ(&sub_path_c);
1127 }
1126 }1128 }
11271129
1128 pub const deleteFileC = @compileError("deprecated: renamed to deleteFileZ");1130 pub const deleteFileC = @compileError("deprecated: renamed to deleteFileZ");
...@@ -1131,6 +1133,17 @@ pub const Dir = struct {...@@ -1131,6 +1133,17 @@ pub const Dir = struct {
1131 pub fn deleteFileZ(self: Dir, sub_path_c: [*:0]const u8) DeleteFileError!void {1133 pub fn deleteFileZ(self: Dir, sub_path_c: [*:0]const u8) DeleteFileError!void {
1132 os.unlinkatZ(self.fd, sub_path_c, 0) catch |err| switch (err) {1134 os.unlinkatZ(self.fd, sub_path_c, 0) catch |err| switch (err) {
1133 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR1135 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
1136 error.AccessDenied => |e| switch (builtin.os.tag) {
1137 // non-Linux POSIX systems return EPERM when trying to delete a directory, so
1138 // we need to handle that case specifically and translate the error
1139 .macosx, .ios, .freebsd, .netbsd, .dragonfly => {
1140 // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them)
1141 const fstat = os.fstatatZ(self.fd, sub_path_c, os.AT_SYMLINK_NOFOLLOW) catch return e;
1142 const is_dir = fstat.mode & os.S_IFMT == os.S_IFDIR;
1143 return if (is_dir) error.IsDir else e;
1144 },
1145 else => return e,
1146 },
1134 else => |e| return e,1147 else => |e| return e,
1135 };1148 };
1136 }1149 }
...@@ -1229,14 +1242,9 @@ pub const Dir = struct {...@@ -1229,14 +1242,9 @@ pub const Dir = struct {
1229 var file = try self.openFile(file_path, .{});1242 var file = try self.openFile(file_path, .{});
1230 defer file.close();1243 defer file.close();
12311244
1232 const size = math.cast(usize, try file.getEndPos()) catch math.maxInt(usize);1245 const stat_size = try file.getEndPos();
1233 if (size > max_bytes) return error.FileTooBig;
1234
1235 const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);
1236 errdefer allocator.free(buf);
12371246
1238 try file.inStream().readNoEof(buf);1247 return file.readAllAllocOptions(allocator, stat_size, max_bytes, alignment, optional_sentinel);
1239 return buf;
1240 }1248 }
12411249
1242 pub const DeleteTreeError = error{1250 pub const DeleteTreeError = error{
...@@ -1532,9 +1540,9 @@ pub const Dir = struct {...@@ -1532,9 +1540,9 @@ pub const Dir = struct {
15321540
1533 var size: ?u64 = null;1541 var size: ?u64 = null;
1534 const mode = options.override_mode orelse blk: {1542 const mode = options.override_mode orelse blk: {
1535 const stat = try in_file.stat();1543 const st = try in_file.stat();
1536 size = stat.size;1544 size = st.size;
1537 break :blk stat.mode;1545 break :blk st.mode;
1538 };1546 };
15391547
1540 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });1548 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
...@@ -1560,6 +1568,17 @@ pub const Dir = struct {...@@ -1560,6 +1568,17 @@ pub const Dir = struct {
1560 return AtomicFile.init(dest_path, options.mode, self, false);1568 return AtomicFile.init(dest_path, options.mode, self, false);
1561 }1569 }
1562 }1570 }
1571
1572 pub const Stat = File.Stat;
1573 pub const StatError = File.StatError;
1574
1575 pub fn stat(self: Dir) StatError!Stat {
1576 const file: File = .{
1577 .handle = self.fd,
1578 .capable_io_mode = .blocking,
1579 };
1580 return file.stat();
1581 }
1563};1582};
15641583
1565/// Returns an handle to the current working directory. It is not opened with iteration capability.1584/// Returns an handle to the current working directory. It is not opened with iteration capability.
...@@ -1808,7 +1827,7 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {...@@ -1808,7 +1827,7 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {
1808 // TODO(#4812): Investigate other systems and whether it is possible to get1827 // TODO(#4812): Investigate other systems and whether it is possible to get
1809 // this path by trying larger and larger buffers until one succeeds.1828 // this path by trying larger and larger buffers until one succeeds.
1810 var buf: [MAX_PATH_BYTES]u8 = undefined;1829 var buf: [MAX_PATH_BYTES]u8 = undefined;
1811 return mem.dupe(allocator, u8, try selfExePath(&buf));1830 return allocator.dupe(u8, try selfExePath(&buf));
1812}1831}
18131832
1814/// Get the path to the current executable.1833/// Get the path to the current executable.
...@@ -1871,7 +1890,7 @@ pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {...@@ -1871,7 +1890,7 @@ pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {
1871 // TODO(#4812): Investigate other systems and whether it is possible to get1890 // TODO(#4812): Investigate other systems and whether it is possible to get
1872 // this path by trying larger and larger buffers until one succeeds.1891 // this path by trying larger and larger buffers until one succeeds.
1873 var buf: [MAX_PATH_BYTES]u8 = undefined;1892 var buf: [MAX_PATH_BYTES]u8 = undefined;
1874 return mem.dupe(allocator, u8, try selfExeDirPath(&buf));1893 return allocator.dupe(u8, try selfExeDirPath(&buf));
1875}1894}
18761895
1877/// Get the directory path that contains the current executable.1896/// Get the directory path that contains the current executable.
...@@ -1893,7 +1912,7 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {...@@ -1893,7 +1912,7 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
1893 // paths. musl supports passing NULL but restricts the output to PATH_MAX1912 // paths. musl supports passing NULL but restricts the output to PATH_MAX
1894 // anyway.1913 // anyway.
1895 var buf: [MAX_PATH_BYTES]u8 = undefined;1914 var buf: [MAX_PATH_BYTES]u8 = undefined;
1896 return mem.dupe(allocator, u8, try os.realpath(pathname, &buf));1915 return allocator.dupe(u8, try os.realpath(pathname, &buf));
1897}1916}
18981917
1899test "" {1918test "" {
lib/std/fs/file.zig+64-2
...@@ -29,6 +29,18 @@ pub const File = struct {...@@ -29,6 +29,18 @@ pub const File = struct {
29 pub const Mode = os.mode_t;29 pub const Mode = os.mode_t;
30 pub const INode = os.ino_t;30 pub const INode = os.ino_t;
3131
32 pub const Kind = enum {
33 BlockDevice,
34 CharacterDevice,
35 Directory,
36 NamedPipe,
37 SymLink,
38 File,
39 UnixDomainSocket,
40 Whiteout,
41 Unknown,
42 };
43
32 pub const default_mode = switch (builtin.os.tag) {44 pub const default_mode = switch (builtin.os.tag) {
33 .windows => 0,45 .windows => 0,
34 .wasi => 0,46 .wasi => 0,
...@@ -209,7 +221,7 @@ pub const File = struct {...@@ -209,7 +221,7 @@ pub const File = struct {
209 /// TODO: integrate with async I/O221 /// TODO: integrate with async I/O
210 pub fn mode(self: File) ModeError!Mode {222 pub fn mode(self: File) ModeError!Mode {
211 if (builtin.os.tag == .windows) {223 if (builtin.os.tag == .windows) {
212 return {};224 return 0;
213 }225 }
214 return (try self.stat()).mode;226 return (try self.stat()).mode;
215 }227 }
...@@ -219,13 +231,14 @@ pub const File = struct {...@@ -219,13 +231,14 @@ pub const File = struct {
219 /// unique across time, as some file systems may reuse an inode after its file has been deleted.231 /// unique across time, as some file systems may reuse an inode after its file has been deleted.
220 /// Some systems may change the inode of a file over time.232 /// Some systems may change the inode of a file over time.
221 ///233 ///
222 /// On Linux, the inode _is_ structure that stores the metadata, and the inode _number_ is what234 /// On Linux, the inode is a structure that stores the metadata, and the inode _number_ is what
223 /// you see here: the index number of the inode.235 /// you see here: the index number of the inode.
224 ///236 ///
225 /// The FileIndex on Windows is similar. It is a number for a file that is unique to each filesystem.237 /// The FileIndex on Windows is similar. It is a number for a file that is unique to each filesystem.
226 inode: INode,238 inode: INode,
227 size: u64,239 size: u64,
228 mode: Mode,240 mode: Mode,
241 kind: Kind,
229242
230 /// Access time in nanoseconds, relative to UTC 1970-01-01.243 /// Access time in nanoseconds, relative to UTC 1970-01-01.
231 atime: i128,244 atime: i128,
...@@ -254,6 +267,7 @@ pub const File = struct {...@@ -254,6 +267,7 @@ pub const File = struct {
254 .inode = info.InternalInformation.IndexNumber,267 .inode = info.InternalInformation.IndexNumber,
255 .size = @bitCast(u64, info.StandardInformation.EndOfFile),268 .size = @bitCast(u64, info.StandardInformation.EndOfFile),
256 .mode = 0,269 .mode = 0,
270 .kind = if (info.StandardInformation.Directory == 0) .File else .Directory,
257 .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),271 .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
258 .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),272 .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
259 .ctime = windows.fromSysTime(info.BasicInformation.CreationTime),273 .ctime = windows.fromSysTime(info.BasicInformation.CreationTime),
...@@ -268,6 +282,27 @@ pub const File = struct {...@@ -268,6 +282,27 @@ pub const File = struct {
268 .inode = st.ino,282 .inode = st.ino,
269 .size = @bitCast(u64, st.size),283 .size = @bitCast(u64, st.size),
270 .mode = st.mode,284 .mode = st.mode,
285 .kind = switch (builtin.os.tag) {
286 .wasi => switch (st.filetype) {
287 os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice,
288 os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice,
289 os.FILETYPE_DIRECTORY => Kind.Directory,
290 os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink,
291 os.FILETYPE_REGULAR_FILE => Kind.File,
292 os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket,
293 else => Kind.Unknown,
294 },
295 else => switch (st.mode & os.S_IFMT) {
296 os.S_IFBLK => Kind.BlockDevice,
297 os.S_IFCHR => Kind.CharacterDevice,
298 os.S_IFDIR => Kind.Directory,
299 os.S_IFIFO => Kind.NamedPipe,
300 os.S_IFLNK => Kind.SymLink,
301 os.S_IFREG => Kind.File,
302 os.S_IFSOCK => Kind.UnixDomainSocket,
303 else => Kind.Unknown,
304 },
305 },
271 .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,306 .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
272 .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,307 .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
273 .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,308 .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
...@@ -306,6 +341,33 @@ pub const File = struct {...@@ -306,6 +341,33 @@ pub const File = struct {
306 try os.futimens(self.handle, &times);341 try os.futimens(self.handle, &times);
307 }342 }
308343
344 /// On success, caller owns returned buffer.
345 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
346 pub fn readAllAlloc(self: File, allocator: *mem.Allocator, stat_size: u64, max_bytes: usize) ![]u8 {
347 return self.readAllAllocOptions(allocator, stat_size, max_bytes, @alignOf(u8), null);
348 }
349
350 /// On success, caller owns returned buffer.
351 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
352 /// Allows specifying alignment and a sentinel value.
353 pub fn readAllAllocOptions(
354 self: File,
355 allocator: *mem.Allocator,
356 stat_size: u64,
357 max_bytes: usize,
358 comptime alignment: u29,
359 comptime optional_sentinel: ?u8,
360 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {
361 const size = math.cast(usize, stat_size) catch math.maxInt(usize);
362 if (size > max_bytes) return error.FileTooBig;
363
364 const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);
365 errdefer allocator.free(buf);
366
367 try self.reader().readNoEof(buf);
368 return buf;
369 }
370
309 pub const ReadError = os.ReadError;371 pub const ReadError = os.ReadError;
310 pub const PReadError = os.PReadError;372 pub const PReadError = os.PReadError;
311373
lib/std/fs/path.zig+2-2
...@@ -1034,7 +1034,7 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8)...@@ -1034,7 +1034,7 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8)
1034 var from_it = mem.tokenize(resolved_from, "/\\");1034 var from_it = mem.tokenize(resolved_from, "/\\");
1035 var to_it = mem.tokenize(resolved_to, "/\\");1035 var to_it = mem.tokenize(resolved_to, "/\\");
1036 while (true) {1036 while (true) {
1037 const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());1037 const from_component = from_it.next() orelse return allocator.dupe(u8, to_it.rest());
1038 const to_rest = to_it.rest();1038 const to_rest = to_it.rest();
1039 if (to_it.next()) |to_component| {1039 if (to_it.next()) |to_component| {
1040 // TODO ASCII is wrong, we actually need full unicode support to compare paths.1040 // TODO ASCII is wrong, we actually need full unicode support to compare paths.
...@@ -1085,7 +1085,7 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![...@@ -1085,7 +1085,7 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![
1085 var from_it = mem.tokenize(resolved_from, "/");1085 var from_it = mem.tokenize(resolved_from, "/");
1086 var to_it = mem.tokenize(resolved_to, "/");1086 var to_it = mem.tokenize(resolved_to, "/");
1087 while (true) {1087 while (true) {
1088 const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());1088 const from_component = from_it.next() orelse return allocator.dupe(u8, to_it.rest());
1089 const to_rest = to_it.rest();1089 const to_rest = to_it.rest();
1090 if (to_it.next()) |to_component| {1090 if (to_it.next()) |to_component| {
1091 if (mem.eql(u8, from_component, to_component))1091 if (mem.eql(u8, from_component, to_component))
lib/std/fs/test.zig+310-3
...@@ -1,7 +1,157 @@...@@ -1,7 +1,157 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const testing = std.testing;
2const builtin = std.builtin;3const builtin = std.builtin;
3const fs = std.fs;4const fs = std.fs;
5const mem = std.mem;
6const wasi = std.os.wasi;
7
8const ArenaAllocator = std.heap.ArenaAllocator;
9const Dir = std.fs.Dir;
4const File = std.fs.File;10const File = std.fs.File;
11const tmpDir = testing.tmpDir;
12
13test "Dir.Iterator" {
14 var tmp_dir = tmpDir(.{ .iterate = true });
15 defer tmp_dir.cleanup();
16
17 // First, create a couple of entries to iterate over.
18 const file = try tmp_dir.dir.createFile("some_file", .{});
19 file.close();
20
21 try tmp_dir.dir.makeDir("some_dir");
22
23 var arena = ArenaAllocator.init(testing.allocator);
24 defer arena.deinit();
25
26 var entries = std.ArrayList(Dir.Entry).init(&arena.allocator);
27
28 // Create iterator.
29 var iter = tmp_dir.dir.iterate();
30 while (try iter.next()) |entry| {
31 // We cannot just store `entry` as on Windows, we're re-using the name buffer
32 // which means we'll actually share the `name` pointer between entries!
33 const name = try arena.allocator.dupe(u8, entry.name);
34 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
35 }
36
37 testing.expect(entries.items.len == 2); // note that the Iterator skips '.' and '..'
38 testing.expect(contains(&entries, Dir.Entry{ .name = "some_file", .kind = Dir.Entry.Kind.File }));
39 testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
40}
41
42fn entry_eql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
43 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
44}
45
46fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
47 for (entries.items) |entry| {
48 if (entry_eql(entry, el)) return true;
49 }
50 return false;
51}
52
53test "readAllAlloc" {
54 var tmp_dir = tmpDir(.{});
55 defer tmp_dir.cleanup();
56
57 var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
58 defer file.close();
59
60 const buf1 = try file.readAllAlloc(testing.allocator, 0, 1024);
61 defer testing.allocator.free(buf1);
62 testing.expect(buf1.len == 0);
63
64 const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";
65 try file.writeAll(write_buf);
66 try file.seekTo(0);
67 const file_size = try file.getEndPos();
68
69 // max_bytes > file_size
70 const buf2 = try file.readAllAlloc(testing.allocator, file_size, 1024);
71 defer testing.allocator.free(buf2);
72 testing.expectEqual(write_buf.len, buf2.len);
73 testing.expect(std.mem.eql(u8, write_buf, buf2));
74 try file.seekTo(0);
75
76 // max_bytes == file_size
77 const buf3 = try file.readAllAlloc(testing.allocator, file_size, write_buf.len);
78 defer testing.allocator.free(buf3);
79 testing.expectEqual(write_buf.len, buf3.len);
80 testing.expect(std.mem.eql(u8, write_buf, buf3));
81
82 // max_bytes < file_size
83 testing.expectError(error.FileTooBig, file.readAllAlloc(testing.allocator, file_size, write_buf.len - 1));
84}
85
86test "directory operations on files" {
87 var tmp_dir = tmpDir(.{});
88 defer tmp_dir.cleanup();
89
90 const test_file_name = "test_file";
91
92 var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true });
93 file.close();
94
95 testing.expectError(error.PathAlreadyExists, tmp_dir.dir.makeDir(test_file_name));
96 testing.expectError(error.NotDir, tmp_dir.dir.openDir(test_file_name, .{}));
97 testing.expectError(error.NotDir, tmp_dir.dir.deleteDir(test_file_name));
98
99 if (builtin.os.tag != .wasi) {
100 // TODO: use Dir's realpath function once that exists
101 const absolute_path = blk: {
102 const relative_path = try fs.path.join(testing.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..], test_file_name });
103 defer testing.allocator.free(relative_path);
104 break :blk try fs.realpathAlloc(testing.allocator, relative_path);
105 };
106 defer testing.allocator.free(absolute_path);
107
108 testing.expectError(error.PathAlreadyExists, fs.makeDirAbsolute(absolute_path));
109 testing.expectError(error.NotDir, fs.deleteDirAbsolute(absolute_path));
110 }
111
112 // ensure the file still exists and is a file as a sanity check
113 file = try tmp_dir.dir.openFile(test_file_name, .{});
114 const stat = try file.stat();
115 testing.expect(stat.kind == .File);
116 file.close();
117}
118
119test "file operations on directories" {
120 var tmp_dir = tmpDir(.{});
121 defer tmp_dir.cleanup();
122
123 const test_dir_name = "test_dir";
124
125 try tmp_dir.dir.makeDir(test_dir_name);
126
127 testing.expectError(error.IsDir, tmp_dir.dir.createFile(test_dir_name, .{}));
128 testing.expectError(error.IsDir, tmp_dir.dir.deleteFile(test_dir_name));
129 // Currently, WASI will return error.Unexpected (via ENOTCAPABLE) when attempting fd_read on a directory handle.
130 // TODO: Re-enable on WASI once https://github.com/bytecodealliance/wasmtime/issues/1935 is resolved.
131 if (builtin.os.tag != .wasi) {
132 testing.expectError(error.IsDir, tmp_dir.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize)));
133 }
134 // Note: The `.write = true` is necessary to ensure the error occurs on all platforms.
135 // TODO: Add a read-only test as well, see https://github.com/ziglang/zig/issues/5732
136 testing.expectError(error.IsDir, tmp_dir.dir.openFile(test_dir_name, .{ .write = true }));
137
138 if (builtin.os.tag != .wasi) {
139 // TODO: use Dir's realpath function once that exists
140 const absolute_path = blk: {
141 const relative_path = try fs.path.join(testing.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..], test_dir_name });
142 defer testing.allocator.free(relative_path);
143 break :blk try fs.realpathAlloc(testing.allocator, relative_path);
144 };
145 defer testing.allocator.free(absolute_path);
146
147 testing.expectError(error.IsDir, fs.createFileAbsolute(absolute_path, .{}));
148 testing.expectError(error.IsDir, fs.deleteFileAbsolute(absolute_path));
149 }
150
151 // ensure the directory still exists as a sanity check
152 var dir = try tmp_dir.dir.openDir(test_dir_name, .{});
153 dir.close();
154}
5155
6test "openSelfExe" {156test "openSelfExe" {
7 if (builtin.os.tag == .wasi) return error.SkipZigTest;157 if (builtin.os.tag == .wasi) return error.SkipZigTest;
...@@ -10,6 +160,163 @@ test "openSelfExe" {...@@ -10,6 +160,163 @@ test "openSelfExe" {
10 self_exe_file.close();160 self_exe_file.close();
11}161}
12162
163test "makePath, put some files in it, deleteTree" {
164 var tmp = tmpDir(.{});
165 defer tmp.cleanup();
166
167 try tmp.dir.makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
168 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
169 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
170 try tmp.dir.deleteTree("os_test_tmp");
171 if (tmp.dir.openDir("os_test_tmp", .{})) |dir| {
172 @panic("expected error");
173 } else |err| {
174 testing.expect(err == error.FileNotFound);
175 }
176}
177
178test "access file" {
179 if (builtin.os.tag == .wasi) return error.SkipZigTest;
180
181 var tmp = tmpDir(.{});
182 defer tmp.cleanup();
183
184 try tmp.dir.makePath("os_test_tmp");
185 if (tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| {
186 @panic("expected error");
187 } else |err| {
188 testing.expect(err == error.FileNotFound);
189 }
190
191 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
192 try tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{});
193 try tmp.dir.deleteTree("os_test_tmp");
194}
195
196test "sendfile" {
197 var tmp = tmpDir(.{});
198 defer tmp.cleanup();
199
200 try tmp.dir.makePath("os_test_tmp");
201 defer tmp.dir.deleteTree("os_test_tmp") catch {};
202
203 var dir = try tmp.dir.openDir("os_test_tmp", .{});
204 defer dir.close();
205
206 const line1 = "line1\n";
207 const line2 = "second line\n";
208 var vecs = [_]std.os.iovec_const{
209 .{
210 .iov_base = line1,
211 .iov_len = line1.len,
212 },
213 .{
214 .iov_base = line2,
215 .iov_len = line2.len,
216 },
217 };
218
219 var src_file = try dir.createFile("sendfile1.txt", .{ .read = true });
220 defer src_file.close();
221
222 try src_file.writevAll(&vecs);
223
224 var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true });
225 defer dest_file.close();
226
227 const header1 = "header1\n";
228 const header2 = "second header\n";
229 const trailer1 = "trailer1\n";
230 const trailer2 = "second trailer\n";
231 var hdtr = [_]std.os.iovec_const{
232 .{
233 .iov_base = header1,
234 .iov_len = header1.len,
235 },
236 .{
237 .iov_base = header2,
238 .iov_len = header2.len,
239 },
240 .{
241 .iov_base = trailer1,
242 .iov_len = trailer1.len,
243 },
244 .{
245 .iov_base = trailer2,
246 .iov_len = trailer2.len,
247 },
248 };
249
250 var written_buf: [100]u8 = undefined;
251 try dest_file.writeFileAll(src_file, .{
252 .in_offset = 1,
253 .in_len = 10,
254 .headers_and_trailers = &hdtr,
255 .header_count = 2,
256 });
257 const amt = try dest_file.preadAll(&written_buf, 0);
258 testing.expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
259}
260
261test "fs.copyFile" {
262 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
263 const src_file = "tmp_test_copy_file.txt";
264 const dest_file = "tmp_test_copy_file2.txt";
265 const dest_file2 = "tmp_test_copy_file3.txt";
266
267 var tmp = tmpDir(.{});
268 defer tmp.cleanup();
269
270 try tmp.dir.writeFile(src_file, data);
271 defer tmp.dir.deleteFile(src_file) catch {};
272
273 try tmp.dir.copyFile(src_file, tmp.dir, dest_file, .{});
274 defer tmp.dir.deleteFile(dest_file) catch {};
275
276 try tmp.dir.copyFile(src_file, tmp.dir, dest_file2, .{ .override_mode = File.default_mode });
277 defer tmp.dir.deleteFile(dest_file2) catch {};
278
279 try expectFileContents(tmp.dir, dest_file, data);
280 try expectFileContents(tmp.dir, dest_file2, data);
281}
282
283fn expectFileContents(dir: Dir, file_path: []const u8, data: []const u8) !void {
284 const contents = try dir.readFileAlloc(testing.allocator, file_path, 1000);
285 defer testing.allocator.free(contents);
286
287 testing.expectEqualSlices(u8, data, contents);
288}
289
290test "AtomicFile" {
291 const test_out_file = "tmp_atomic_file_test_dest.txt";
292 const test_content =
293 \\ hello!
294 \\ this is a test file
295 ;
296
297 var tmp = tmpDir(.{});
298 defer tmp.cleanup();
299
300 {
301 var af = try tmp.dir.atomicFile(test_out_file, .{});
302 defer af.deinit();
303 try af.file.writeAll(test_content);
304 try af.finish();
305 }
306 const content = try tmp.dir.readFileAlloc(testing.allocator, test_out_file, 9999);
307 defer testing.allocator.free(content);
308 testing.expect(mem.eql(u8, content, test_content));
309
310 try tmp.dir.deleteFile(test_out_file);
311}
312
313test "realpath" {
314 if (builtin.os.tag == .wasi) return error.SkipZigTest;
315
316 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
317 testing.expectError(error.FileNotFound, fs.realpath("definitely_bogus_does_not_exist1234", &buf));
318}
319
13const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.ns_per_ms;320const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.ns_per_ms;
14321
15test "open file with exclusive nonblocking lock twice" {322test "open file with exclusive nonblocking lock twice" {
...@@ -116,7 +423,7 @@ test "create file, lock and read from multiple process at once" {...@@ -116,7 +423,7 @@ test "create file, lock and read from multiple process at once" {
116test "open file with exclusive nonblocking lock twice (absolute paths)" {423test "open file with exclusive nonblocking lock twice (absolute paths)" {
117 if (builtin.os.tag == .wasi) return error.SkipZigTest;424 if (builtin.os.tag == .wasi) return error.SkipZigTest;
118425
119 const allocator = std.testing.allocator;426 const allocator = testing.allocator;
120427
121 const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};428 const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};
122 const filename = try fs.path.resolve(allocator, &file_paths);429 const filename = try fs.path.resolve(allocator, &file_paths);
...@@ -126,7 +433,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {...@@ -126,7 +433,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
126433
127 const file2 = fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });434 const file2 = fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
128 file1.close();435 file1.close();
129 std.testing.expectError(error.WouldBlock, file2);436 testing.expectError(error.WouldBlock, file2);
130437
131 try fs.deleteFileAbsolute(filename);438 try fs.deleteFileAbsolute(filename);
132}439}
...@@ -187,7 +494,7 @@ const FileLockTestContext = struct {...@@ -187,7 +494,7 @@ const FileLockTestContext = struct {
187};494};
188495
189fn run_lock_file_test(contexts: []FileLockTestContext) !void {496fn run_lock_file_test(contexts: []FileLockTestContext) !void {
190 var threads = std.ArrayList(*std.Thread).init(std.testing.allocator);497 var threads = std.ArrayList(*std.Thread).init(testing.allocator);
191 defer {498 defer {
192 for (threads.items) |thread| {499 for (threads.items) |thread| {
193 thread.wait();500 thread.wait();
lib/std/fs/wasi.zig+51-37
...@@ -1,17 +1,44 @@...@@ -1,17 +1,44 @@
1const std = @import("std");1const std = @import("std");
2const os = std.os;2const os = std.os;
3const mem = std.mem;3const mem = std.mem;
4const math = std.math;
4const Allocator = mem.Allocator;5const Allocator = mem.Allocator;
56
6usingnamespace std.os.wasi;7usingnamespace std.os.wasi;
78
8/// Type of WASI preopen.9/// Type-tag of WASI preopen.
9///10///
10/// WASI currently offers only `Dir` as a valid preopen resource.11/// WASI currently offers only `Dir` as a valid preopen resource.
11pub const PreopenType = enum {12pub const PreopenTypeTag = enum {
12 Dir,13 Dir,
13};14};
1415
16/// Type of WASI preopen.
17///
18/// WASI currently offers only `Dir` as a valid preopen resource.
19pub const PreopenType = union(PreopenTypeTag) {
20 /// Preopened directory type.
21 Dir: []const u8,
22
23 const Self = @This();
24
25 pub fn eql(self: Self, other: PreopenType) bool {
26 if (!mem.eql(u8, @tagName(self), @tagName(other))) return false;
27
28 switch (self) {
29 PreopenTypeTag.Dir => |this_path| return mem.eql(u8, this_path, other.Dir),
30 }
31 }
32
33 pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype) !void {
34 try out_stream.print("PreopenType{{ ", .{});
35 switch (self) {
36 PreopenType.Dir => |path| try out_stream.print(".Dir = '{}'", .{path}),
37 }
38 return out_stream.print(" }}", .{});
39 }
40};
41
15/// WASI preopen struct. This struct consists of a WASI file descriptor42/// WASI preopen struct. This struct consists of a WASI file descriptor
16/// and type of WASI preopen. It can be obtained directly from the WASI43/// and type of WASI preopen. It can be obtained directly from the WASI
17/// runtime using `PreopenList.populate()` method.44/// runtime using `PreopenList.populate()` method.
...@@ -20,29 +47,15 @@ pub const Preopen = struct {...@@ -20,29 +47,15 @@ pub const Preopen = struct {
20 fd: fd_t,47 fd: fd_t,
2148
22 /// Type of the preopen.49 /// Type of the preopen.
23 @"type": union(PreopenType) {50 @"type": PreopenType,
24 /// Path to a preopened directory.
25 Dir: []const u8,
26 },
2751
28 const Self = @This();52 /// Construct new `Preopen` instance.
2953 pub fn new(fd: fd_t, preopen_type: PreopenType) Preopen {
30 /// Construct new `Preopen` instance of type `PreopenType.Dir` from54 return Preopen{
31 /// WASI file descriptor and WASI path.
32 pub fn newDir(fd: fd_t, path: []const u8) Self {
33 return Self{
34 .fd = fd,55 .fd = fd,
35 .@"type" = .{ .Dir = path },56 .@"type" = preopen_type,
36 };57 };
37 }58 }
38
39 pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: var) !void {
40 try out_stream.print("{{ .fd = {}, ", .{self.fd});
41 switch (self.@"type") {
42 PreopenType.Dir => |path| try out_stream.print(".Dir = '{}'", .{path}),
43 }
44 return out_stream.print(" }}", .{});
45 }
46};59};
4760
48/// Dynamically-sized array list of WASI preopens. This struct is a61/// Dynamically-sized array list of WASI preopens. This struct is a
...@@ -60,7 +73,7 @@ pub const PreopenList = struct {...@@ -60,7 +73,7 @@ pub const PreopenList = struct {
6073
61 const Self = @This();74 const Self = @This();
6275
63 pub const Error = os.UnexpectedError || Allocator.Error;76 pub const Error = error{ OutOfMemory, Overflow } || os.UnexpectedError;
6477
65 /// Deinitialize with `deinit`.78 /// Deinitialize with `deinit`.
66 pub fn init(allocator: *Allocator) Self {79 pub fn init(allocator: *Allocator) Self {
...@@ -82,6 +95,12 @@ pub const PreopenList = struct {...@@ -82,6 +95,12 @@ pub const PreopenList = struct {
82 ///95 ///
83 /// If called more than once, it will clear its contents every time before96 /// If called more than once, it will clear its contents every time before
84 /// issuing the syscalls.97 /// issuing the syscalls.
98 ///
99 /// In the unlinkely event of overflowing the number of available file descriptors,
100 /// returns `error.Overflow`. In this case, even though an error condition was reached
101 /// the preopen list still contains all valid preopened file descriptors that are valid
102 /// for use. Therefore, it is fine to call `find`, `asSlice`, or `toOwnedSlice`. Finally,
103 /// `deinit` still must be called!
85 pub fn populate(self: *Self) Error!void {104 pub fn populate(self: *Self) Error!void {
86 // Clear contents if we're being called again105 // Clear contents if we're being called again
87 for (self.toOwnedSlice()) |preopen| {106 for (self.toOwnedSlice()) |preopen| {
...@@ -98,6 +117,7 @@ pub const PreopenList = struct {...@@ -98,6 +117,7 @@ pub const PreopenList = struct {
98 ESUCCESS => {},117 ESUCCESS => {},
99 ENOTSUP => {118 ENOTSUP => {
100 // not a preopen, so keep going119 // not a preopen, so keep going
120 fd = try math.add(fd_t, fd, 1);
101 continue;121 continue;
102 },122 },
103 EBADF => {123 EBADF => {
...@@ -113,24 +133,18 @@ pub const PreopenList = struct {...@@ -113,24 +133,18 @@ pub const PreopenList = struct {
113 ESUCCESS => {},133 ESUCCESS => {},
114 else => |err| return os.unexpectedErrno(err),134 else => |err| return os.unexpectedErrno(err),
115 }135 }
116 const preopen = Preopen.newDir(fd, path_buf);136 const preopen = Preopen.new(fd, PreopenType{ .Dir = path_buf });
117 try self.buffer.append(preopen);137 try self.buffer.append(preopen);
118 fd += 1;138 fd = try math.add(fd_t, fd, 1);
119 }139 }
120 }140 }
121141
122 /// Find preopen by path. If the preopen exists, return it.142 /// Find preopen by type. If the preopen exists, return it.
123 /// Otherwise, return `null`.143 /// Otherwise, return `null`.
124 ///144 pub fn find(self: Self, preopen_type: PreopenType) ?*const Preopen {
125 /// TODO make the function more generic by searching by `PreopenType` union. This will145 for (self.buffer.items) |*preopen| {
126 /// be needed in the future when WASI extends its capabilities to resources146 if (preopen.@"type".eql(preopen_type)) {
127 /// other than preopened directories.147 return preopen;
128 pub fn find(self: Self, path: []const u8) ?*const Preopen {
129 for (self.buffer.items) |preopen| {
130 switch (preopen.@"type") {
131 PreopenType.Dir => |preopen_path| {
132 if (mem.eql(u8, path, preopen_path)) return &preopen;
133 },
134 }148 }
135 }149 }
136 return null;150 return null;
...@@ -156,7 +170,7 @@ test "extracting WASI preopens" {...@@ -156,7 +170,7 @@ test "extracting WASI preopens" {
156 try preopens.populate();170 try preopens.populate();
157171
158 std.testing.expectEqual(@as(usize, 1), preopens.asSlice().len);172 std.testing.expectEqual(@as(usize, 1), preopens.asSlice().len);
159 const preopen = preopens.find(".") orelse unreachable;173 const preopen = preopens.find(PreopenType{ .Dir = "." }) orelse unreachable;
160 std.testing.expect(std.mem.eql(u8, ".", preopen.@"type".Dir));174 std.testing.expect(preopen.@"type".eql(PreopenType{ .Dir = "." }));
161 std.testing.expectEqual(@as(usize, 3), preopen.fd);175 std.testing.expectEqual(@as(usize, 3), preopen.fd);
162}176}
lib/std/fs/watch.zig+1-1
...@@ -360,7 +360,7 @@ pub fn Watch(comptime V: type) type {...@@ -360,7 +360,7 @@ pub fn Watch(comptime V: type) type {
360360
361 fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {361 fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {
362 // TODO we might need to convert dirname and basename to canonical file paths ("short"?)362 // TODO we might need to convert dirname and basename to canonical file paths ("short"?)
363 const dirname = try std.mem.dupe(self.allocator, u8, std.fs.path.dirname(file_path) orelse ".");363 const dirname = try self.allocator.dupe(u8, std.fs.path.dirname(file_path) orelse ".");
364 var dirname_consumed = false;364 var dirname_consumed = false;
365 defer if (!dirname_consumed) self.allocator.free(dirname);365 defer if (!dirname_consumed) self.allocator.free(dirname);
366366
lib/std/hash/auto_hash.zig+8-8
...@@ -21,7 +21,7 @@ pub const HashStrategy = enum {...@@ -21,7 +21,7 @@ pub const HashStrategy = enum {
21};21};
2222
23/// Helper function to hash a pointer and mutate the strategy if needed.23/// Helper function to hash a pointer and mutate the strategy if needed.
24pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void {24pub fn hashPointer(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
25 const info = @typeInfo(@TypeOf(key));25 const info = @typeInfo(@TypeOf(key));
2626
27 switch (info.Pointer.size) {27 switch (info.Pointer.size) {
...@@ -53,7 +53,7 @@ pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void {...@@ -53,7 +53,7 @@ pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void {
53}53}
5454
55/// Helper function to hash a set of contiguous objects, from an array or slice.55/// Helper function to hash a set of contiguous objects, from an array or slice.
56pub fn hashArray(hasher: var, key: var, comptime strat: HashStrategy) void {56pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
57 switch (strat) {57 switch (strat) {
58 .Shallow => {58 .Shallow => {
59 // TODO detect via a trait when Key has no padding bits to59 // TODO detect via a trait when Key has no padding bits to
...@@ -73,7 +73,7 @@ pub fn hashArray(hasher: var, key: var, comptime strat: HashStrategy) void {...@@ -73,7 +73,7 @@ pub fn hashArray(hasher: var, key: var, comptime strat: HashStrategy) void {
7373
74/// Provides generic hashing for any eligible type.74/// Provides generic hashing for any eligible type.
75/// Strategy is provided to determine if pointers should be followed or not.75/// Strategy is provided to determine if pointers should be followed or not.
76pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void {76pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
77 const Key = @TypeOf(key);77 const Key = @TypeOf(key);
78 switch (@typeInfo(Key)) {78 switch (@typeInfo(Key)) {
79 .NoReturn,79 .NoReturn,
...@@ -161,7 +161,7 @@ pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void {...@@ -161,7 +161,7 @@ pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void {
161/// Provides generic hashing for any eligible type.161/// Provides generic hashing for any eligible type.
162/// Only hashes `key` itself, pointers are not followed.162/// Only hashes `key` itself, pointers are not followed.
163/// Slices are rejected to avoid ambiguity on the user's intention.163/// Slices are rejected to avoid ambiguity on the user's intention.
164pub fn autoHash(hasher: var, key: var) void {164pub fn autoHash(hasher: anytype, key: anytype) void {
165 const Key = @TypeOf(key);165 const Key = @TypeOf(key);
166 if (comptime meta.trait.isSlice(Key)) {166 if (comptime meta.trait.isSlice(Key)) {
167 comptime assert(@hasDecl(std, "StringHashMap")); // detect when the following message needs updated167 comptime assert(@hasDecl(std, "StringHashMap")); // detect when the following message needs updated
...@@ -181,28 +181,28 @@ pub fn autoHash(hasher: var, key: var) void {...@@ -181,28 +181,28 @@ pub fn autoHash(hasher: var, key: var) void {
181const testing = std.testing;181const testing = std.testing;
182const Wyhash = std.hash.Wyhash;182const Wyhash = std.hash.Wyhash;
183183
184fn testHash(key: var) u64 {184fn testHash(key: anytype) u64 {
185 // Any hash could be used here, for testing autoHash.185 // Any hash could be used here, for testing autoHash.
186 var hasher = Wyhash.init(0);186 var hasher = Wyhash.init(0);
187 hash(&hasher, key, .Shallow);187 hash(&hasher, key, .Shallow);
188 return hasher.final();188 return hasher.final();
189}189}
190190
191fn testHashShallow(key: var) u64 {191fn testHashShallow(key: anytype) u64 {
192 // Any hash could be used here, for testing autoHash.192 // Any hash could be used here, for testing autoHash.
193 var hasher = Wyhash.init(0);193 var hasher = Wyhash.init(0);
194 hash(&hasher, key, .Shallow);194 hash(&hasher, key, .Shallow);
195 return hasher.final();195 return hasher.final();
196}196}
197197
198fn testHashDeep(key: var) u64 {198fn testHashDeep(key: anytype) u64 {
199 // Any hash could be used here, for testing autoHash.199 // Any hash could be used here, for testing autoHash.
200 var hasher = Wyhash.init(0);200 var hasher = Wyhash.init(0);
201 hash(&hasher, key, .Deep);201 hash(&hasher, key, .Deep);
202 return hasher.final();202 return hasher.final();
203}203}
204204
205fn testHashDeepRecursive(key: var) u64 {205fn testHashDeepRecursive(key: anytype) u64 {
206 // Any hash could be used here, for testing autoHash.206 // Any hash could be used here, for testing autoHash.
207 var hasher = Wyhash.init(0);207 var hasher = Wyhash.init(0);
208 hash(&hasher, key, .DeepRecursive);208 hash(&hasher, key, .DeepRecursive);
lib/std/hash/benchmark.zig+5-5
...@@ -88,7 +88,7 @@ const Result = struct {...@@ -88,7 +88,7 @@ const Result = struct {
8888
89const block_size: usize = 8 * 8192;89const block_size: usize = 8 * 8192;
9090
91pub fn benchmarkHash(comptime H: var, bytes: usize) !Result {91pub fn benchmarkHash(comptime H: anytype, bytes: usize) !Result {
92 var h = blk: {92 var h = blk: {
93 if (H.init_u8s) |init| {93 if (H.init_u8s) |init| {
94 break :blk H.ty.init(init);94 break :blk H.ty.init(init);
...@@ -119,7 +119,7 @@ pub fn benchmarkHash(comptime H: var, bytes: usize) !Result {...@@ -119,7 +119,7 @@ pub fn benchmarkHash(comptime H: var, bytes: usize) !Result {
119 };119 };
120}120}
121121
122pub fn benchmarkHashSmallKeys(comptime H: var, key_size: usize, bytes: usize) !Result {122pub fn benchmarkHashSmallKeys(comptime H: anytype, key_size: usize, bytes: usize) !Result {
123 const key_count = bytes / key_size;123 const key_count = bytes / key_size;
124 var block: [block_size]u8 = undefined;124 var block: [block_size]u8 = undefined;
125 prng.random.bytes(block[0..]);125 prng.random.bytes(block[0..]);
...@@ -172,7 +172,7 @@ fn mode(comptime x: comptime_int) comptime_int {...@@ -172,7 +172,7 @@ fn mode(comptime x: comptime_int) comptime_int {
172}172}
173173
174pub fn main() !void {174pub fn main() !void {
175 const stdout = std.io.getStdOut().outStream();175 const stdout = std.io.getStdOut().writer();
176176
177 var buffer: [1024]u8 = undefined;177 var buffer: [1024]u8 = undefined;
178 var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);178 var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
...@@ -248,13 +248,13 @@ pub fn main() !void {...@@ -248,13 +248,13 @@ pub fn main() !void {
248 if (H.has_iterative_api) {248 if (H.has_iterative_api) {
249 prng.seed(seed);249 prng.seed(seed);
250 const result = try benchmarkHash(H, count);250 const result = try benchmarkHash(H, count);
251 try stdout.print(" iterative: {:4} MiB/s [{x:0<16}]\n", .{ result.throughput / (1 * MiB), result.hash });251 try stdout.print(" iterative: {:5} MiB/s [{x:0<16}]\n", .{ result.throughput / (1 * MiB), result.hash });
252 }252 }
253253
254 if (!test_iterative_only) {254 if (!test_iterative_only) {
255 prng.seed(seed);255 prng.seed(seed);
256 const result_small = try benchmarkHashSmallKeys(H, key_size, count);256 const result_small = try benchmarkHashSmallKeys(H, key_size, count);
257 try stdout.print(" small keys: {:4} MiB/s [{x:0<16}]\n", .{ result_small.throughput / (1 * MiB), result_small.hash });257 try stdout.print(" small keys: {:5} MiB/s [{x:0<16}]\n", .{ result_small.throughput / (1 * MiB), result_small.hash });
258 }258 }
259 }259 }
260 }260 }
lib/std/hash/cityhash.zig+1-1
...@@ -354,7 +354,7 @@ pub const CityHash64 = struct {...@@ -354,7 +354,7 @@ pub const CityHash64 = struct {
354 }354 }
355};355};
356356
357fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 {357fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {
358 const hashbytes = hashbits / 8;358 const hashbytes = hashbits / 8;
359 var key: [256]u8 = undefined;359 var key: [256]u8 = undefined;
360 var hashes: [hashbytes * 256]u8 = undefined;360 var hashes: [hashbytes * 256]u8 = undefined;
lib/std/hash/murmur.zig+1-1
...@@ -279,7 +279,7 @@ pub const Murmur3_32 = struct {...@@ -279,7 +279,7 @@ pub const Murmur3_32 = struct {
279 }279 }
280};280};
281281
282fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 {282fn SMHasherTest(comptime hash_fn: anytype, comptime hashbits: u32) u32 {
283 const hashbytes = hashbits / 8;283 const hashbytes = hashbits / 8;
284 var key: [256]u8 = undefined;284 var key: [256]u8 = undefined;
285 var hashes: [hashbytes * 256]u8 = undefined;285 var hashes: [hashbytes * 256]u8 = undefined;
lib/std/hash_map.zig+791-310
...@@ -9,17 +9,23 @@ const autoHash = std.hash.autoHash;...@@ -9,17 +9,23 @@ const autoHash = std.hash.autoHash;
9const Wyhash = std.hash.Wyhash;9const Wyhash = std.hash.Wyhash;
10const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
11const builtin = @import("builtin");11const builtin = @import("builtin");
1212const hash_map = @This();
13const want_modification_safety = std.debug.runtime_safety;
14const debug_u32 = if (want_modification_safety) u32 else void;
1513
16pub fn AutoHashMap(comptime K: type, comptime V: type) type {14pub fn AutoHashMap(comptime K: type, comptime V: type) type {
17 return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K));15 return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K), autoEqlIsCheap(K));
16}
17
18pub fn AutoHashMapUnmanaged(comptime K: type, comptime V: type) type {
19 return HashMapUnmanaged(K, V, getAutoHashFn(K), getAutoEqlFn(K), autoEqlIsCheap(K));
18}20}
1921
20/// Builtin hashmap for strings as keys.22/// Builtin hashmap for strings as keys.
21pub fn StringHashMap(comptime V: type) type {23pub fn StringHashMap(comptime V: type) type {
22 return HashMap([]const u8, V, hashString, eqlString);24 return HashMap([]const u8, V, hashString, eqlString, true);
25}
26
27pub fn StringHashMapUnmanaged(comptime V: type) type {
28 return HashMapUnmanaged([]const u8, V, hashString, eqlString, true);
23}29}
2430
25pub fn eqlString(a: []const u8, b: []const u8) bool {31pub fn eqlString(a: []const u8, b: []const u8) bool {
...@@ -30,422 +36,860 @@ pub fn hashString(s: []const u8) u32 {...@@ -30,422 +36,860 @@ pub fn hashString(s: []const u8) u32 {
30 return @truncate(u32, std.hash.Wyhash.hash(0, s));36 return @truncate(u32, std.hash.Wyhash.hash(0, s));
31}37}
3238
33pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {39/// Insertion order is preserved.
40/// Deletions perform a "swap removal" on the entries list.
41/// Modifying the hash map while iterating is allowed, however one must understand
42/// the (well defined) behavior when mixing insertions and deletions with iteration.
43/// For a hash map that can be initialized directly that does not store an Allocator
44/// field, see `HashMapUnmanaged`.
45/// When `store_hash` is `false`, this data structure is biased towards cheap `eql`
46/// functions. It does not store each item's hash in the table. Setting `store_hash`
47/// to `true` incurs slightly more memory cost by storing each key's hash in the table
48/// but only has to call `eql` for hash collisions.
49pub fn HashMap(
50 comptime K: type,
51 comptime V: type,
52 comptime hash: fn (key: K) u32,
53 comptime eql: fn (a: K, b: K) bool,
54 comptime store_hash: bool,
55) type {
34 return struct {56 return struct {
35 entries: []Entry,57 unmanaged: Unmanaged,
36 size: usize,
37 max_distance_from_start_index: usize,
38 allocator: *Allocator,58 allocator: *Allocator,
3959
40 /// This is used to detect bugs where a hashtable is edited while an iterator is running.60 pub const Unmanaged = HashMapUnmanaged(K, V, hash, eql, store_hash);
41 modification_count: debug_u32,61 pub const Entry = Unmanaged.Entry;
4262 pub const Hash = Unmanaged.Hash;
43 const Self = @This();63 pub const GetOrPutResult = Unmanaged.GetOrPutResult;
44
45 /// A *KV is a mutable pointer into this HashMap's internal storage.
46 /// Modifying the key is undefined behavior.
47 /// Modifying the value is harmless.
48 /// *KV pointers become invalid whenever this HashMap is modified,
49 /// and then any access to the *KV is undefined behavior.
50 pub const KV = struct {
51 key: K,
52 value: V,
53 };
54
55 const Entry = struct {
56 used: bool,
57 distance_from_start_index: usize,
58 kv: KV,
59 };
60
61 pub const GetOrPutResult = struct {
62 kv: *KV,
63 found_existing: bool,
64 };
6564
65 /// Deprecated. Iterate using `items`.
66 pub const Iterator = struct {66 pub const Iterator = struct {
67 hm: *const Self,67 hm: *const Self,
68 // how many items have we returned68 /// Iterator through the entry array.
69 count: usize,
70 // iterator through the entry array
71 index: usize,69 index: usize,
72 // used to detect concurrent modification
73 initial_modification_count: debug_u32,
7470
75 pub fn next(it: *Iterator) ?*KV {71 pub fn next(it: *Iterator) ?*Entry {
76 if (want_modification_safety) {72 if (it.index >= it.hm.unmanaged.entries.items.len) return null;
77 assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification73 const result = &it.hm.unmanaged.entries.items[it.index];
78 }74 it.index += 1;
79 if (it.count >= it.hm.size) return null;75 return result;
80 while (it.index < it.hm.entries.len) : (it.index += 1) {
81 const entry = &it.hm.entries[it.index];
82 if (entry.used) {
83 it.index += 1;
84 it.count += 1;
85 return &entry.kv;
86 }
87 }
88 unreachable; // no next item
89 }76 }
9077
91 // Reset the iterator to the initial index78 /// Reset the iterator to the initial index
92 pub fn reset(it: *Iterator) void {79 pub fn reset(it: *Iterator) void {
93 it.count = 0;
94 it.index = 0;80 it.index = 0;
95 // Resetting the modification count too
96 it.initial_modification_count = it.hm.modification_count;
97 }81 }
98 };82 };
9983
84 const Self = @This();
85 const Index = Unmanaged.Index;
86
100 pub fn init(allocator: *Allocator) Self {87 pub fn init(allocator: *Allocator) Self {
101 return Self{88 return .{
102 .entries = &[_]Entry{},89 .unmanaged = .{},
103 .allocator = allocator,90 .allocator = allocator,
104 .size = 0,
105 .max_distance_from_start_index = 0,
106 .modification_count = if (want_modification_safety) 0 else {},
107 };91 };
108 }92 }
10993
110 pub fn deinit(hm: Self) void {94 pub fn deinit(self: *Self) void {
111 hm.allocator.free(hm.entries);95 self.unmanaged.deinit(self.allocator);
96 self.* = undefined;
112 }97 }
11398
114 pub fn clear(hm: *Self) void {99 pub fn clearRetainingCapacity(self: *Self) void {
115 for (hm.entries) |*entry| {100 return self.unmanaged.clearRetainingCapacity();
116 entry.used = false;
117 }
118 hm.size = 0;
119 hm.max_distance_from_start_index = 0;
120 hm.incrementModificationCount();
121 }101 }
122102
103 pub fn clearAndFree(self: *Self) void {
104 return self.unmanaged.clearAndFree(self.allocator);
105 }
106
107 /// Deprecated. Use `items().len`.
123 pub fn count(self: Self) usize {108 pub fn count(self: Self) usize {
124 return self.size;109 return self.items().len;
110 }
111
112 /// Deprecated. Iterate using `items`.
113 pub fn iterator(self: *const Self) Iterator {
114 return Iterator{
115 .hm = self,
116 .index = 0,
117 };
125 }118 }
126119
127 /// If key exists this function cannot fail.120 /// If key exists this function cannot fail.
128 /// If there is an existing item with `key`, then the result121 /// If there is an existing item with `key`, then the result
129 /// kv pointer points to it, and found_existing is true.122 /// `Entry` pointer points to it, and found_existing is true.
130 /// Otherwise, puts a new item with undefined value, and123 /// Otherwise, puts a new item with undefined value, and
131 /// the kv pointer points to it. Caller should then initialize124 /// the `Entry` pointer points to it. Caller should then initialize
132 /// the data.125 /// the value (but not the key).
133 pub fn getOrPut(self: *Self, key: K) !GetOrPutResult {126 pub fn getOrPut(self: *Self, key: K) !GetOrPutResult {
134 // TODO this implementation can be improved - we should only127 return self.unmanaged.getOrPut(self.allocator, key);
135 // have to hash once and find the entry once.
136 if (self.get(key)) |kv| {
137 return GetOrPutResult{
138 .kv = kv,
139 .found_existing = true,
140 };
141 }
142 self.incrementModificationCount();
143 try self.autoCapacity();
144 const put_result = self.internalPut(key);
145 assert(put_result.old_kv == null);
146 return GetOrPutResult{
147 .kv = &put_result.new_entry.kv,
148 .found_existing = false,
149 };
150 }128 }
151129
152 pub fn getOrPutValue(self: *Self, key: K, value: V) !*KV {130 /// If there is an existing item with `key`, then the result
153 const res = try self.getOrPut(key);131 /// `Entry` pointer points to it, and found_existing is true.
154 if (!res.found_existing)132 /// Otherwise, puts a new item with undefined value, and
155 res.kv.value = value;133 /// the `Entry` pointer points to it. Caller should then initialize
134 /// the value (but not the key).
135 /// If a new entry needs to be stored, this function asserts there
136 /// is enough capacity to store it.
137 pub fn getOrPutAssumeCapacity(self: *Self, key: K) GetOrPutResult {
138 return self.unmanaged.getOrPutAssumeCapacity(key);
139 }
140
141 pub fn getOrPutValue(self: *Self, key: K, value: V) !*Entry {
142 return self.unmanaged.getOrPutValue(self.allocator, key, value);
143 }
144
145 /// Increases capacity, guaranteeing that insertions up until the
146 /// `expected_count` will not cause an allocation, and therefore cannot fail.
147 pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {
148 return self.unmanaged.ensureCapacity(self.allocator, new_capacity);
149 }
150
151 /// Returns the number of total elements which may be present before it is
152 /// no longer guaranteed that no allocations will be performed.
153 pub fn capacity(self: *Self) usize {
154 return self.unmanaged.capacity();
155 }
156
157 /// Clobbers any existing data. To detect if a put would clobber
158 /// existing data, see `getOrPut`.
159 pub fn put(self: *Self, key: K, value: V) !void {
160 return self.unmanaged.put(self.allocator, key, value);
161 }
162
163 /// Inserts a key-value pair into the hash map, asserting that no previous
164 /// entry with the same key is already present
165 pub fn putNoClobber(self: *Self, key: K, value: V) !void {
166 return self.unmanaged.putNoClobber(self.allocator, key, value);
167 }
168
169 /// Asserts there is enough capacity to store the new key-value pair.
170 /// Clobbers any existing data. To detect if a put would clobber
171 /// existing data, see `getOrPutAssumeCapacity`.
172 pub fn putAssumeCapacity(self: *Self, key: K, value: V) void {
173 return self.unmanaged.putAssumeCapacity(key, value);
174 }
175
176 /// Asserts there is enough capacity to store the new key-value pair.
177 /// Asserts that it does not clobber any existing data.
178 /// To detect if a put would clobber existing data, see `getOrPutAssumeCapacity`.
179 pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void {
180 return self.unmanaged.putAssumeCapacityNoClobber(key, value);
181 }
156182
157 return res.kv;183 /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
184 pub fn fetchPut(self: *Self, key: K, value: V) !?Entry {
185 return self.unmanaged.fetchPut(self.allocator, key, value);
158 }186 }
159187
160 fn optimizedCapacity(expected_count: usize) usize {188 /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
161 // ensure that the hash map will be at most 60% full if189 /// If insertion happuns, asserts there is enough capacity without allocating.
162 // expected_count items are put into it190 pub fn fetchPutAssumeCapacity(self: *Self, key: K, value: V) ?Entry {
163 var optimized_capacity = expected_count * 5 / 3;191 return self.unmanaged.fetchPutAssumeCapacity(key, value);
164 // an overflow here would mean the amount of memory required would not
165 // be representable in the address space
166 return math.ceilPowerOfTwo(usize, optimized_capacity) catch unreachable;
167 }192 }
168193
169 /// Increases capacity so that the hash map will be at most194 pub fn getEntry(self: Self, key: K) ?*Entry {
170 /// 60% full when expected_count items are put into it195 return self.unmanaged.getEntry(key);
171 pub fn ensureCapacity(self: *Self, expected_count: usize) !void {
172 if (expected_count == 0) return;
173 const optimized_capacity = optimizedCapacity(expected_count);
174 return self.ensureCapacityExact(optimized_capacity);
175 }196 }
176197
177 /// Sets the capacity to the new capacity if the new198 pub fn get(self: Self, key: K) ?V {
178 /// capacity is greater than the current capacity.199 return self.unmanaged.get(key);
179 /// New capacity must be a power of two.200 }
180 fn ensureCapacityExact(self: *Self, new_capacity: usize) !void {201
181 // capacity must always be a power of two to allow for modulo202 pub fn contains(self: Self, key: K) bool {
182 // optimization in the constrainIndex fn203 return self.unmanaged.contains(key);
183 assert(math.isPowerOfTwo(new_capacity));204 }
205
206 /// If there is an `Entry` with a matching key, it is deleted from
207 /// the hash map, and then returned from this function.
208 pub fn remove(self: *Self, key: K) ?Entry {
209 return self.unmanaged.remove(key);
210 }
211
212 /// Asserts there is an `Entry` with matching key, deletes it from the hash map,
213 /// and discards it.
214 pub fn removeAssertDiscard(self: *Self, key: K) void {
215 return self.unmanaged.removeAssertDiscard(key);
216 }
217
218 pub fn items(self: Self) []Entry {
219 return self.unmanaged.items();
220 }
221
222 pub fn clone(self: Self) !Self {
223 var other = try self.unmanaged.clone(self.allocator);
224 return other.promote(self.allocator);
225 }
226 };
227}
228
229/// General purpose hash table.
230/// Insertion order is preserved.
231/// Deletions perform a "swap removal" on the entries list.
232/// Modifying the hash map while iterating is allowed, however one must understand
233/// the (well defined) behavior when mixing insertions and deletions with iteration.
234/// This type does not store an Allocator field - the Allocator must be passed in
235/// with each function call that requires it. See `HashMap` for a type that stores
236/// an Allocator field for convenience.
237/// Can be initialized directly using the default field values.
238/// This type is designed to have low overhead for small numbers of entries. When
239/// `store_hash` is `false` and the number of entries in the map is less than 9,
240/// the overhead cost of using `HashMapUnmanaged` rather than `std.ArrayList` is
241/// only a single pointer-sized integer.
242/// When `store_hash` is `false`, this data structure is biased towards cheap `eql`
243/// functions. It does not store each item's hash in the table. Setting `store_hash`
244/// to `true` incurs slightly more memory cost by storing each key's hash in the table
245/// but guarantees only one call to `eql` per insertion/deletion.
246pub fn HashMapUnmanaged(
247 comptime K: type,
248 comptime V: type,
249 comptime hash: fn (key: K) u32,
250 comptime eql: fn (a: K, b: K) bool,
251 comptime store_hash: bool,
252) type {
253 return struct {
254 /// It is permitted to access this field directly.
255 entries: std.ArrayListUnmanaged(Entry) = .{},
256
257 /// When entries length is less than `linear_scan_max`, this remains `null`.
258 /// Once entries length grows big enough, this field is allocated. There is
259 /// an IndexHeader followed by an array of Index(I) structs, where I is defined
260 /// by how many total indexes there are.
261 index_header: ?*IndexHeader = null,
262
263 /// Modifying the key is illegal behavior.
264 /// Modifying the value is allowed.
265 /// Entry pointers become invalid whenever this HashMap is modified,
266 /// unless `ensureCapacity` was previously used.
267 pub const Entry = struct {
268 /// This field is `void` if `store_hash` is `false`.
269 hash: Hash,
270 key: K,
271 value: V,
272 };
273
274 pub const Hash = if (store_hash) u32 else void;
275
276 pub const GetOrPutResult = struct {
277 entry: *Entry,
278 found_existing: bool,
279 };
280
281 pub const Managed = HashMap(K, V, hash, eql, store_hash);
282
283 const Self = @This();
284
285 const linear_scan_max = 8;
184286
185 if (new_capacity <= self.entries.len) {287 pub fn promote(self: Self, allocator: *Allocator) Managed {
186 return;288 return .{
289 .unmanaged = self,
290 .allocator = allocator,
291 };
292 }
293
294 pub fn deinit(self: *Self, allocator: *Allocator) void {
295 self.entries.deinit(allocator);
296 if (self.index_header) |header| {
297 header.free(allocator);
298 }
299 self.* = undefined;
300 }
301
302 pub fn clearRetainingCapacity(self: *Self) void {
303 self.entries.items.len = 0;
304 if (self.index_header) |header| {
305 header.max_distance_from_start_index = 0;
306 switch (header.capacityIndexType()) {
307 .u8 => mem.set(Index(u8), header.indexes(u8), Index(u8).empty),
308 .u16 => mem.set(Index(u16), header.indexes(u16), Index(u16).empty),
309 .u32 => mem.set(Index(u32), header.indexes(u32), Index(u32).empty),
310 .usize => mem.set(Index(usize), header.indexes(usize), Index(usize).empty),
311 }
312 }
313 }
314
315 pub fn clearAndFree(self: *Self, allocator: *Allocator) void {
316 self.entries.shrink(allocator, 0);
317 if (self.index_header) |header| {
318 header.free(allocator);
319 self.index_header = null;
187 }320 }
321 }
322
323 /// If key exists this function cannot fail.
324 /// If there is an existing item with `key`, then the result
325 /// `Entry` pointer points to it, and found_existing is true.
326 /// Otherwise, puts a new item with undefined value, and
327 /// the `Entry` pointer points to it. Caller should then initialize
328 /// the value (but not the key).
329 pub fn getOrPut(self: *Self, allocator: *Allocator, key: K) !GetOrPutResult {
330 self.ensureCapacity(allocator, self.entries.items.len + 1) catch |err| {
331 // "If key exists this function cannot fail."
332 return GetOrPutResult{
333 .entry = self.getEntry(key) orelse return err,
334 .found_existing = true,
335 };
336 };
337 return self.getOrPutAssumeCapacity(key);
338 }
188339
189 const old_entries = self.entries;340 /// If there is an existing item with `key`, then the result
190 try self.initCapacity(new_capacity);341 /// `Entry` pointer points to it, and found_existing is true.
191 self.incrementModificationCount();342 /// Otherwise, puts a new item with undefined value, and
192 if (old_entries.len > 0) {343 /// the `Entry` pointer points to it. Caller should then initialize
193 // dump all of the old elements into the new table344 /// the value (but not the key).
194 for (old_entries) |*old_entry| {345 /// If a new entry needs to be stored, this function asserts there
195 if (old_entry.used) {346 /// is enough capacity to store it.
196 self.internalPut(old_entry.kv.key).new_entry.kv.value = old_entry.kv.value;347 pub fn getOrPutAssumeCapacity(self: *Self, key: K) GetOrPutResult {
348 const header = self.index_header orelse {
349 // Linear scan.
350 const h = if (store_hash) hash(key) else {};
351 for (self.entries.items) |*item| {
352 if (item.hash == h and eql(key, item.key)) {
353 return GetOrPutResult{
354 .entry = item,
355 .found_existing = true,
356 };
197 }357 }
198 }358 }
199 self.allocator.free(old_entries);359 const new_entry = self.entries.addOneAssumeCapacity();
360 new_entry.* = .{
361 .hash = if (store_hash) h else {},
362 .key = key,
363 .value = undefined,
364 };
365 return GetOrPutResult{
366 .entry = new_entry,
367 .found_existing = false,
368 };
369 };
370
371 switch (header.capacityIndexType()) {
372 .u8 => return self.getOrPutInternal(key, header, u8),
373 .u16 => return self.getOrPutInternal(key, header, u16),
374 .u32 => return self.getOrPutInternal(key, header, u32),
375 .usize => return self.getOrPutInternal(key, header, usize),
376 }
377 }
378
379 pub fn getOrPutValue(self: *Self, allocator: *Allocator, key: K, value: V) !*Entry {
380 const res = try self.getOrPut(allocator, key);
381 if (!res.found_existing)
382 res.entry.value = value;
383
384 return res.entry;
385 }
386
387 /// Increases capacity, guaranteeing that insertions up until the
388 /// `expected_count` will not cause an allocation, and therefore cannot fail.
389 pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void {
390 try self.entries.ensureCapacity(allocator, new_capacity);
391 if (new_capacity <= linear_scan_max) return;
392
393 // Ensure that the indexes will be at most 60% full if
394 // `new_capacity` items are put into it.
395 const needed_len = new_capacity * 5 / 3;
396 if (self.index_header) |header| {
397 if (needed_len > header.indexes_len) {
398 // An overflow here would mean the amount of memory required would not
399 // be representable in the address space.
400 const new_indexes_len = math.ceilPowerOfTwo(usize, needed_len) catch unreachable;
401 const new_header = try IndexHeader.alloc(allocator, new_indexes_len);
402 self.insertAllEntriesIntoNewHeader(new_header);
403 header.free(allocator);
404 self.index_header = new_header;
405 }
406 } else {
407 // An overflow here would mean the amount of memory required would not
408 // be representable in the address space.
409 const new_indexes_len = math.ceilPowerOfTwo(usize, needed_len) catch unreachable;
410 const header = try IndexHeader.alloc(allocator, new_indexes_len);
411 self.insertAllEntriesIntoNewHeader(header);
412 self.index_header = header;
200 }413 }
201 }414 }
202415
203 /// Returns the kv pair that was already there.416 /// Returns the number of total elements which may be present before it is
204 pub fn put(self: *Self, key: K, value: V) !?KV {417 /// no longer guaranteed that no allocations will be performed.
205 try self.autoCapacity();418 pub fn capacity(self: Self) usize {
206 return putAssumeCapacity(self, key, value);419 const entry_cap = self.entries.capacity;
420 const header = self.index_header orelse return math.min(linear_scan_max, entry_cap);
421 const indexes_cap = (header.indexes_len + 1) * 3 / 4;
422 return math.min(entry_cap, indexes_cap);
207 }423 }
208424
209 /// Calls put() and asserts that no kv pair is clobbered.425 /// Clobbers any existing data. To detect if a put would clobber
210 pub fn putNoClobber(self: *Self, key: K, value: V) !void {426 /// existing data, see `getOrPut`.
211 assert((try self.put(key, value)) == null);427 pub fn put(self: *Self, allocator: *Allocator, key: K, value: V) !void {
428 const result = try self.getOrPut(allocator, key);
429 result.entry.value = value;
212 }430 }
213431
214 pub fn putAssumeCapacity(self: *Self, key: K, value: V) ?KV {432 /// Inserts a key-value pair into the hash map, asserting that no previous
215 assert(self.count() < self.entries.len);433 /// entry with the same key is already present
216 self.incrementModificationCount();434 pub fn putNoClobber(self: *Self, allocator: *Allocator, key: K, value: V) !void {
435 const result = try self.getOrPut(allocator, key);
436 assert(!result.found_existing);
437 result.entry.value = value;
438 }
217439
218 const put_result = self.internalPut(key);440 /// Asserts there is enough capacity to store the new key-value pair.
219 put_result.new_entry.kv.value = value;441 /// Clobbers any existing data. To detect if a put would clobber
220 return put_result.old_kv;442 /// existing data, see `getOrPutAssumeCapacity`.
443 pub fn putAssumeCapacity(self: *Self, key: K, value: V) void {
444 const result = self.getOrPutAssumeCapacity(key);
445 result.entry.value = value;
221 }446 }
222447
448 /// Asserts there is enough capacity to store the new key-value pair.
449 /// Asserts that it does not clobber any existing data.
450 /// To detect if a put would clobber existing data, see `getOrPutAssumeCapacity`.
223 pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void {451 pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void {
224 assert(self.putAssumeCapacity(key, value) == null);452 const result = self.getOrPutAssumeCapacity(key);
453 assert(!result.found_existing);
454 result.entry.value = value;
455 }
456
457 /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
458 pub fn fetchPut(self: *Self, allocator: *Allocator, key: K, value: V) !?Entry {
459 const gop = try self.getOrPut(allocator, key);
460 var result: ?Entry = null;
461 if (gop.found_existing) {
462 result = gop.entry.*;
463 }
464 gop.entry.value = value;
465 return result;
225 }466 }
226467
227 pub fn get(hm: *const Self, key: K) ?*KV {468 /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
228 if (hm.entries.len == 0) {469 /// If insertion happens, asserts there is enough capacity without allocating.
470 pub fn fetchPutAssumeCapacity(self: *Self, key: K, value: V) ?Entry {
471 const gop = self.getOrPutAssumeCapacity(key);
472 var result: ?Entry = null;
473 if (gop.found_existing) {
474 result = gop.entry.*;
475 }
476 gop.entry.value = value;
477 return result;
478 }
479
480 pub fn getEntry(self: Self, key: K) ?*Entry {
481 const header = self.index_header orelse {
482 // Linear scan.
483 const h = if (store_hash) hash(key) else {};
484 for (self.entries.items) |*item| {
485 if (item.hash == h and eql(key, item.key)) {
486 return item;
487 }
488 }
229 return null;489 return null;
490 };
491
492 switch (header.capacityIndexType()) {
493 .u8 => return self.getInternal(key, header, u8),
494 .u16 => return self.getInternal(key, header, u16),
495 .u32 => return self.getInternal(key, header, u32),
496 .usize => return self.getInternal(key, header, usize),
230 }497 }
231 return hm.internalGet(key);
232 }498 }
233499
234 pub fn getValue(hm: *const Self, key: K) ?V {500 pub fn get(self: Self, key: K) ?V {
235 return if (hm.get(key)) |kv| kv.value else null;501 return if (self.getEntry(key)) |entry| entry.value else null;
236 }502 }
237503
238 pub fn contains(hm: *const Self, key: K) bool {504 pub fn contains(self: Self, key: K) bool {
239 return hm.get(key) != null;505 return self.getEntry(key) != null;
240 }506 }
241507
242 /// Returns any kv pair that was removed.508 /// If there is an `Entry` with a matching key, it is deleted from
243 pub fn remove(hm: *Self, key: K) ?KV {509 /// the hash map, and then returned from this function.
244 if (hm.entries.len == 0) return null;510 pub fn remove(self: *Self, key: K) ?Entry {
245 hm.incrementModificationCount();511 const header = self.index_header orelse {
246 const start_index = hm.keyToIndex(key);512 // Linear scan.
247 {513 const h = if (store_hash) hash(key) else {};
248 var roll_over: usize = 0;514 for (self.entries.items) |item, i| {
249 while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {515 if (item.hash == h and eql(key, item.key)) {
250 const index = hm.constrainIndex(start_index + roll_over);516 return self.entries.swapRemove(i);
251 var entry = &hm.entries[index];
252
253 if (!entry.used) return null;
254
255 if (!eql(entry.kv.key, key)) continue;
256
257 const removed_kv = entry.kv;
258 while (roll_over < hm.entries.len) : (roll_over += 1) {
259 const next_index = hm.constrainIndex(start_index + roll_over + 1);
260 const next_entry = &hm.entries[next_index];
261 if (!next_entry.used or next_entry.distance_from_start_index == 0) {
262 entry.used = false;
263 hm.size -= 1;
264 return removed_kv;
265 }
266 entry.* = next_entry.*;
267 entry.distance_from_start_index -= 1;
268 entry = next_entry;
269 }517 }
270 unreachable; // shifting everything in the table
271 }518 }
519 return null;
520 };
521 switch (header.capacityIndexType()) {
522 .u8 => return self.removeInternal(key, header, u8),
523 .u16 => return self.removeInternal(key, header, u16),
524 .u32 => return self.removeInternal(key, header, u32),
525 .usize => return self.removeInternal(key, header, usize),
272 }526 }
273 return null;
274 }527 }
275528
276 /// Calls remove(), asserts that a kv pair is removed, and discards it.529 /// Asserts there is an `Entry` with matching key, deletes it from the hash map,
277 pub fn removeAssertDiscard(hm: *Self, key: K) void {530 /// and discards it.
278 assert(hm.remove(key) != null);531 pub fn removeAssertDiscard(self: *Self, key: K) void {
532 assert(self.remove(key) != null);
279 }533 }
280534
281 pub fn iterator(hm: *const Self) Iterator {535 pub fn items(self: Self) []Entry {
282 return Iterator{536 return self.entries.items;
283 .hm = hm,
284 .count = 0,
285 .index = 0,
286 .initial_modification_count = hm.modification_count,
287 };
288 }537 }
289538
290 pub fn clone(self: Self) !Self {539 pub fn clone(self: Self, allocator: *Allocator) !Self {
291 var other = Self.init(self.allocator);540 var other: Self = .{};
292 try other.initCapacity(self.entries.len);541 try other.entries.appendSlice(allocator, self.entries.items);
293 var it = self.iterator();542
294 while (it.next()) |entry| {543 if (self.index_header) |header| {
295 try other.putNoClobber(entry.key, entry.value);544 const new_header = try IndexHeader.alloc(allocator, header.indexes_len);
545 other.insertAllEntriesIntoNewHeader(new_header);
546 other.index_header = new_header;
296 }547 }
297 return other;548 return other;
298 }549 }
299550
300 fn autoCapacity(self: *Self) !void {551 fn removeInternal(self: *Self, key: K, header: *IndexHeader, comptime I: type) ?Entry {
301 if (self.entries.len == 0) {552 const indexes = header.indexes(I);
302 return self.ensureCapacityExact(16);553 const h = hash(key);
303 }554 const start_index = header.constrainIndex(h);
304 // if we get too full (60%), double the capacity555 var roll_over: usize = 0;
305 if (self.size * 5 >= self.entries.len * 3) {556 while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) {
306 return self.ensureCapacityExact(self.entries.len * 2);557 const index_index = header.constrainIndex(start_index + roll_over);
307 }558 var index = &indexes[index_index];
308 }559 if (index.isEmpty())
560 return null;
309561
310 fn initCapacity(hm: *Self, capacity: usize) !void {562 const entry = &self.entries.items[index.entry_index];
311 hm.entries = try hm.allocator.alloc(Entry, capacity);563
312 hm.size = 0;564 const hash_match = if (store_hash) h == entry.hash else true;
313 hm.max_distance_from_start_index = 0;565 if (!hash_match or !eql(key, entry.key))
314 for (hm.entries) |*entry| {566 continue;
315 entry.used = false;567
568 const removed_entry = self.entries.swapRemove(index.entry_index);
569 if (self.entries.items.len > 0 and self.entries.items.len != index.entry_index) {
570 // Because of the swap remove, now we need to update the index that was
571 // pointing to the last entry and is now pointing to this removed item slot.
572 self.updateEntryIndex(header, self.entries.items.len, index.entry_index, I, indexes);
573 }
574
575 // Now we have to shift over the following indexes.
576 roll_over += 1;
577 while (roll_over < header.indexes_len) : (roll_over += 1) {
578 const next_index_index = header.constrainIndex(start_index + roll_over);
579 const next_index = &indexes[next_index_index];
580 if (next_index.isEmpty() or next_index.distance_from_start_index == 0) {
581 index.setEmpty();
582 return removed_entry;
583 }
584 index.* = next_index.*;
585 index.distance_from_start_index -= 1;
586 index = next_index;
587 }
588 unreachable;
316 }589 }
590 return null;
317 }591 }
318592
319 fn incrementModificationCount(hm: *Self) void {593 fn updateEntryIndex(
320 if (want_modification_safety) {594 self: *Self,
321 hm.modification_count +%= 1;595 header: *IndexHeader,
596 old_entry_index: usize,
597 new_entry_index: usize,
598 comptime I: type,
599 indexes: []Index(I),
600 ) void {
601 const h = if (store_hash) self.entries.items[new_entry_index].hash else hash(self.entries.items[new_entry_index].key);
602 const start_index = header.constrainIndex(h);
603 var roll_over: usize = 0;
604 while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) {
605 const index_index = header.constrainIndex(start_index + roll_over);
606 const index = &indexes[index_index];
607 if (index.entry_index == old_entry_index) {
608 index.entry_index = @intCast(I, new_entry_index);
609 return;
610 }
322 }611 }
612 unreachable;
323 }613 }
324614
325 const InternalPutResult = struct {615 /// Must ensureCapacity before calling this.
326 new_entry: *Entry,616 fn getOrPutInternal(self: *Self, key: K, header: *IndexHeader, comptime I: type) GetOrPutResult {
327 old_kv: ?KV,617 const indexes = header.indexes(I);
328 };618 const h = hash(key);
329619 const start_index = header.constrainIndex(h);
330 /// Returns a pointer to the new entry.
331 /// Asserts that there is enough space for the new item.
332 fn internalPut(self: *Self, orig_key: K) InternalPutResult {
333 var key = orig_key;
334 var value: V = undefined;
335 const start_index = self.keyToIndex(key);
336 var roll_over: usize = 0;620 var roll_over: usize = 0;
337 var distance_from_start_index: usize = 0;621 var distance_from_start_index: usize = 0;
338 var got_result_entry = false;622 while (roll_over <= header.indexes_len) : ({
339 var result = InternalPutResult{
340 .new_entry = undefined,
341 .old_kv = null,
342 };
343 while (roll_over < self.entries.len) : ({
344 roll_over += 1;623 roll_over += 1;
345 distance_from_start_index += 1;624 distance_from_start_index += 1;
346 }) {625 }) {
347 const index = self.constrainIndex(start_index + roll_over);626 const index_index = header.constrainIndex(start_index + roll_over);
348 const entry = &self.entries[index];627 const index = indexes[index_index];
349628 if (index.isEmpty()) {
350 if (entry.used and !eql(entry.kv.key, key)) {629 indexes[index_index] = .{
351 if (entry.distance_from_start_index < distance_from_start_index) {630 .distance_from_start_index = @intCast(I, distance_from_start_index),
352 // robin hood to the rescue631 .entry_index = @intCast(I, self.entries.items.len),
353 const tmp = entry.*;632 };
354 self.max_distance_from_start_index = math.max(self.max_distance_from_start_index, distance_from_start_index);633 header.maybeBumpMax(distance_from_start_index);
355 if (!got_result_entry) {634 const new_entry = self.entries.addOneAssumeCapacity();
356 got_result_entry = true;635 new_entry.* = .{
357 result.new_entry = entry;636 .hash = if (store_hash) h else {},
637 .key = key,
638 .value = undefined,
639 };
640 return .{
641 .found_existing = false,
642 .entry = new_entry,
643 };
644 }
645
646 // This pointer survives the following append because we call
647 // entries.ensureCapacity before getOrPutInternal.
648 const entry = &self.entries.items[index.entry_index];
649 const hash_match = if (store_hash) h == entry.hash else true;
650 if (hash_match and eql(key, entry.key)) {
651 return .{
652 .found_existing = true,
653 .entry = entry,
654 };
655 }
656 if (index.distance_from_start_index < distance_from_start_index) {
657 // In this case, we did not find the item. We will put a new entry.
658 // However, we will use this index for the new entry, and move
659 // the previous index down the line, to keep the max_distance_from_start_index
660 // as small as possible.
661 indexes[index_index] = .{
662 .distance_from_start_index = @intCast(I, distance_from_start_index),
663 .entry_index = @intCast(I, self.entries.items.len),
664 };
665 header.maybeBumpMax(distance_from_start_index);
666 const new_entry = self.entries.addOneAssumeCapacity();
667 new_entry.* = .{
668 .hash = if (store_hash) h else {},
669 .key = key,
670 .value = undefined,
671 };
672
673 distance_from_start_index = index.distance_from_start_index;
674 var prev_entry_index = index.entry_index;
675
676 // Find somewhere to put the index we replaced by shifting
677 // following indexes backwards.
678 roll_over += 1;
679 distance_from_start_index += 1;
680 while (roll_over < header.indexes_len) : ({
681 roll_over += 1;
682 distance_from_start_index += 1;
683 }) {
684 const next_index_index = header.constrainIndex(start_index + roll_over);
685 const next_index = indexes[next_index_index];
686 if (next_index.isEmpty()) {
687 header.maybeBumpMax(distance_from_start_index);
688 indexes[next_index_index] = .{
689 .entry_index = prev_entry_index,
690 .distance_from_start_index = @intCast(I, distance_from_start_index),
691 };
692 return .{
693 .found_existing = false,
694 .entry = new_entry,
695 };
696 }
697 if (next_index.distance_from_start_index < distance_from_start_index) {
698 header.maybeBumpMax(distance_from_start_index);
699 indexes[next_index_index] = .{
700 .entry_index = prev_entry_index,
701 .distance_from_start_index = @intCast(I, distance_from_start_index),
702 };
703 distance_from_start_index = next_index.distance_from_start_index;
704 prev_entry_index = next_index.entry_index;
358 }705 }
359 entry.* = Entry{
360 .used = true,
361 .distance_from_start_index = distance_from_start_index,
362 .kv = KV{
363 .key = key,
364 .value = value,
365 },
366 };
367 key = tmp.kv.key;
368 value = tmp.kv.value;
369 distance_from_start_index = tmp.distance_from_start_index;
370 }706 }
371 continue;707 unreachable;
372 }708 }
709 }
710 unreachable;
711 }
373712
374 if (entry.used) {713 fn getInternal(self: Self, key: K, header: *IndexHeader, comptime I: type) ?*Entry {
375 result.old_kv = entry.kv;714 const indexes = header.indexes(I);
376 } else {715 const h = hash(key);
377 // adding an entry. otherwise overwriting old value with716 const start_index = header.constrainIndex(h);
378 // same key717 var roll_over: usize = 0;
379 self.size += 1;718 while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) {
380 }719 const index_index = header.constrainIndex(start_index + roll_over);
720 const index = indexes[index_index];
721 if (index.isEmpty())
722 return null;
723
724 const entry = &self.entries.items[index.entry_index];
725 const hash_match = if (store_hash) h == entry.hash else true;
726 if (hash_match and eql(key, entry.key))
727 return entry;
728 }
729 return null;
730 }
381731
382 self.max_distance_from_start_index = math.max(distance_from_start_index, self.max_distance_from_start_index);732 fn insertAllEntriesIntoNewHeader(self: *Self, header: *IndexHeader) void {
383 if (!got_result_entry) {733 switch (header.capacityIndexType()) {
384 result.new_entry = entry;734 .u8 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u8),
385 }735 .u16 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u16),
386 entry.* = Entry{736 .u32 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u32),
387 .used = true,737 .usize => return self.insertAllEntriesIntoNewHeaderGeneric(header, usize),
388 .distance_from_start_index = distance_from_start_index,
389 .kv = KV{
390 .key = key,
391 .value = value,
392 },
393 };
394 return result;
395 }738 }
396 unreachable; // put into a full map
397 }739 }
398740
399 fn internalGet(hm: Self, key: K) ?*KV {741 fn insertAllEntriesIntoNewHeaderGeneric(self: *Self, header: *IndexHeader, comptime I: type) void {
400 const start_index = hm.keyToIndex(key);742 const indexes = header.indexes(I);
401 {743 entry_loop: for (self.entries.items) |entry, i| {
744 const h = if (store_hash) entry.hash else hash(entry.key);
745 const start_index = header.constrainIndex(h);
746 var entry_index = i;
402 var roll_over: usize = 0;747 var roll_over: usize = 0;
403 while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {748 var distance_from_start_index: usize = 0;
404 const index = hm.constrainIndex(start_index + roll_over);749 while (roll_over < header.indexes_len) : ({
405 const entry = &hm.entries[index];750 roll_over += 1;
406751 distance_from_start_index += 1;
407 if (!entry.used) return null;752 }) {
408 if (eql(entry.kv.key, key)) return &entry.kv;753 const index_index = header.constrainIndex(start_index + roll_over);
754 const next_index = indexes[index_index];
755 if (next_index.isEmpty()) {
756 header.maybeBumpMax(distance_from_start_index);
757 indexes[index_index] = .{
758 .distance_from_start_index = @intCast(I, distance_from_start_index),
759 .entry_index = @intCast(I, entry_index),
760 };
761 continue :entry_loop;
762 }
763 if (next_index.distance_from_start_index < distance_from_start_index) {
764 header.maybeBumpMax(distance_from_start_index);
765 indexes[index_index] = .{
766 .distance_from_start_index = @intCast(I, distance_from_start_index),
767 .entry_index = @intCast(I, entry_index),
768 };
769 distance_from_start_index = next_index.distance_from_start_index;
770 entry_index = next_index.entry_index;
771 }
409 }772 }
773 unreachable;
410 }774 }
411 return null;
412 }775 }
776 };
777}
778
779const CapacityIndexType = enum { u8, u16, u32, usize };
780
781fn capacityIndexType(indexes_len: usize) CapacityIndexType {
782 if (indexes_len < math.maxInt(u8))
783 return .u8;
784 if (indexes_len < math.maxInt(u16))
785 return .u16;
786 if (indexes_len < math.maxInt(u32))
787 return .u32;
788 return .usize;
789}
790
791fn capacityIndexSize(indexes_len: usize) usize {
792 switch (capacityIndexType(indexes_len)) {
793 .u8 => return @sizeOf(Index(u8)),
794 .u16 => return @sizeOf(Index(u16)),
795 .u32 => return @sizeOf(Index(u32)),
796 .usize => return @sizeOf(Index(usize)),
797 }
798}
799
800fn Index(comptime I: type) type {
801 return extern struct {
802 entry_index: I,
803 distance_from_start_index: I,
804
805 const Self = @This();
806
807 const empty = Self{
808 .entry_index = math.maxInt(I),
809 .distance_from_start_index = undefined,
810 };
413811
414 fn keyToIndex(hm: Self, key: K) usize {812 fn isEmpty(idx: Self) bool {
415 return hm.constrainIndex(@as(usize, hash(key)));813 return idx.entry_index == math.maxInt(I);
416 }814 }
417815
418 fn constrainIndex(hm: Self, i: usize) usize {816 fn setEmpty(idx: *Self) void {
419 // this is an optimization for modulo of power of two integers;817 idx.entry_index = math.maxInt(I);
420 // it requires hm.entries.len to always be a power of two
421 return i & (hm.entries.len - 1);
422 }818 }
423 };819 };
424}820}
425821
822/// This struct is trailed by an array of `Index(I)`, where `I`
823/// and the array length are determined by `indexes_len`.
824const IndexHeader = struct {
825 max_distance_from_start_index: usize,
826 indexes_len: usize,
827
828 fn constrainIndex(header: IndexHeader, i: usize) usize {
829 // This is an optimization for modulo of power of two integers;
830 // it requires `indexes_len` to always be a power of two.
831 return i & (header.indexes_len - 1);
832 }
833
834 fn indexes(header: *IndexHeader, comptime I: type) []Index(I) {
835 const start = @ptrCast([*]Index(I), @ptrCast([*]u8, header) + @sizeOf(IndexHeader));
836 return start[0..header.indexes_len];
837 }
838
839 fn capacityIndexType(header: IndexHeader) CapacityIndexType {
840 return hash_map.capacityIndexType(header.indexes_len);
841 }
842
843 fn maybeBumpMax(header: *IndexHeader, distance_from_start_index: usize) void {
844 if (distance_from_start_index > header.max_distance_from_start_index) {
845 header.max_distance_from_start_index = distance_from_start_index;
846 }
847 }
848
849 fn alloc(allocator: *Allocator, len: usize) !*IndexHeader {
850 const index_size = hash_map.capacityIndexSize(len);
851 const nbytes = @sizeOf(IndexHeader) + index_size * len;
852 const bytes = try allocator.allocAdvanced(u8, @alignOf(IndexHeader), nbytes, .exact);
853 @memset(bytes.ptr + @sizeOf(IndexHeader), 0xff, bytes.len - @sizeOf(IndexHeader));
854 const result = @ptrCast(*IndexHeader, bytes.ptr);
855 result.* = .{
856 .max_distance_from_start_index = 0,
857 .indexes_len = len,
858 };
859 return result;
860 }
861
862 fn free(header: *IndexHeader, allocator: *Allocator) void {
863 const index_size = hash_map.capacityIndexSize(header.indexes_len);
864 const ptr = @ptrCast([*]u8, header);
865 const slice = ptr[0 .. @sizeOf(IndexHeader) + header.indexes_len * index_size];
866 allocator.free(slice);
867 }
868};
869
426test "basic hash map usage" {870test "basic hash map usage" {
427 var map = AutoHashMap(i32, i32).init(std.testing.allocator);871 var map = AutoHashMap(i32, i32).init(std.testing.allocator);
428 defer map.deinit();872 defer map.deinit();
429873
430 testing.expect((try map.put(1, 11)) == null);874 testing.expect((try map.fetchPut(1, 11)) == null);
431 testing.expect((try map.put(2, 22)) == null);875 testing.expect((try map.fetchPut(2, 22)) == null);
432 testing.expect((try map.put(3, 33)) == null);876 testing.expect((try map.fetchPut(3, 33)) == null);
433 testing.expect((try map.put(4, 44)) == null);877 testing.expect((try map.fetchPut(4, 44)) == null);
434878
435 try map.putNoClobber(5, 55);879 try map.putNoClobber(5, 55);
436 testing.expect((try map.put(5, 66)).?.value == 55);880 testing.expect((try map.fetchPut(5, 66)).?.value == 55);
437 testing.expect((try map.put(5, 55)).?.value == 66);881 testing.expect((try map.fetchPut(5, 55)).?.value == 66);
438882
439 const gop1 = try map.getOrPut(5);883 const gop1 = try map.getOrPut(5);
440 testing.expect(gop1.found_existing == true);884 testing.expect(gop1.found_existing == true);
441 testing.expect(gop1.kv.value == 55);885 testing.expect(gop1.entry.value == 55);
442 gop1.kv.value = 77;886 gop1.entry.value = 77;
443 testing.expect(map.get(5).?.value == 77);887 testing.expect(map.getEntry(5).?.value == 77);
444888
445 const gop2 = try map.getOrPut(99);889 const gop2 = try map.getOrPut(99);
446 testing.expect(gop2.found_existing == false);890 testing.expect(gop2.found_existing == false);
447 gop2.kv.value = 42;891 gop2.entry.value = 42;
448 testing.expect(map.get(99).?.value == 42);892 testing.expect(map.getEntry(99).?.value == 42);
449893
450 const gop3 = try map.getOrPutValue(5, 5);894 const gop3 = try map.getOrPutValue(5, 5);
451 testing.expect(gop3.value == 77);895 testing.expect(gop3.value == 77);
...@@ -454,15 +898,15 @@ test "basic hash map usage" {...@@ -454,15 +898,15 @@ test "basic hash map usage" {
454 testing.expect(gop4.value == 41);898 testing.expect(gop4.value == 41);
455899
456 testing.expect(map.contains(2));900 testing.expect(map.contains(2));
457 testing.expect(map.get(2).?.value == 22);901 testing.expect(map.getEntry(2).?.value == 22);
458 testing.expect(map.getValue(2).? == 22);902 testing.expect(map.get(2).? == 22);
459903
460 const rmv1 = map.remove(2);904 const rmv1 = map.remove(2);
461 testing.expect(rmv1.?.key == 2);905 testing.expect(rmv1.?.key == 2);
462 testing.expect(rmv1.?.value == 22);906 testing.expect(rmv1.?.value == 22);
463 testing.expect(map.remove(2) == null);907 testing.expect(map.remove(2) == null);
908 testing.expect(map.getEntry(2) == null);
464 testing.expect(map.get(2) == null);909 testing.expect(map.get(2) == null);
465 testing.expect(map.getValue(2) == null);
466910
467 map.removeAssertDiscard(3);911 map.removeAssertDiscard(3);
468}912}
...@@ -498,8 +942,8 @@ test "iterator hash map" {...@@ -498,8 +942,8 @@ test "iterator hash map" {
498 it.reset();942 it.reset();
499943
500 var count: usize = 0;944 var count: usize = 0;
501 while (it.next()) |kv| : (count += 1) {945 while (it.next()) |entry| : (count += 1) {
502 buffer[@intCast(usize, kv.key)] = kv.value;946 buffer[@intCast(usize, entry.key)] = entry.value;
503 }947 }
504 testing.expect(count == 3);948 testing.expect(count == 3);
505 testing.expect(it.next() == null);949 testing.expect(it.next() == null);
...@@ -510,8 +954,8 @@ test "iterator hash map" {...@@ -510,8 +954,8 @@ test "iterator hash map" {
510954
511 it.reset();955 it.reset();
512 count = 0;956 count = 0;
513 while (it.next()) |kv| {957 while (it.next()) |entry| {
514 buffer[@intCast(usize, kv.key)] = kv.value;958 buffer[@intCast(usize, entry.key)] = entry.value;
515 count += 1;959 count += 1;
516 if (count >= 2) break;960 if (count >= 2) break;
517 }961 }
...@@ -531,14 +975,33 @@ test "ensure capacity" {...@@ -531,14 +975,33 @@ test "ensure capacity" {
531 defer map.deinit();975 defer map.deinit();
532976
533 try map.ensureCapacity(20);977 try map.ensureCapacity(20);
534 const initialCapacity = map.entries.len;978 const initial_capacity = map.capacity();
535 testing.expect(initialCapacity >= 20);979 testing.expect(initial_capacity >= 20);
536 var i: i32 = 0;980 var i: i32 = 0;
537 while (i < 20) : (i += 1) {981 while (i < 20) : (i += 1) {
538 testing.expect(map.putAssumeCapacity(i, i + 10) == null);982 testing.expect(map.fetchPutAssumeCapacity(i, i + 10) == null);
539 }983 }
540 // shouldn't resize from putAssumeCapacity984 // shouldn't resize from putAssumeCapacity
541 testing.expect(initialCapacity == map.entries.len);985 testing.expect(initial_capacity == map.capacity());
986}
987
988test "clone" {
989 var original = AutoHashMap(i32, i32).init(std.testing.allocator);
990 defer original.deinit();
991
992 // put more than `linear_scan_max` so we can test that the index header is properly cloned
993 var i: u8 = 0;
994 while (i < 10) : (i += 1) {
995 try original.putNoClobber(i, i * 10);
996 }
997
998 var copy = try original.clone();
999 defer copy.deinit();
1000
1001 i = 0;
1002 while (i < 10) : (i += 1) {
1003 testing.expect(copy.get(i).? == i * 10);
1004 }
542}1005}
5431006
544pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) {1007pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) {
...@@ -575,6 +1038,24 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {...@@ -575,6 +1038,24 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
575 }.eql;1038 }.eql;
576}1039}
5771040
1041pub fn autoEqlIsCheap(comptime K: type) bool {
1042 return switch (@typeInfo(K)) {
1043 .Bool,
1044 .Int,
1045 .Float,
1046 .Pointer,
1047 .ComptimeFloat,
1048 .ComptimeInt,
1049 .Enum,
1050 .Fn,
1051 .ErrorSet,
1052 .AnyFrame,
1053 .EnumLiteral,
1054 => true,
1055 else => false,
1056 };
1057}
1058
578pub fn getAutoHashStratFn(comptime K: type, comptime strategy: std.hash.Strategy) (fn (K) u32) {1059pub fn getAutoHashStratFn(comptime K: type, comptime strategy: std.hash.Strategy) (fn (K) u32) {
579 return struct {1060 return struct {
580 fn hash(key: K) u32 {1061 fn hash(key: K) u32 {
lib/std/heap.zig+287-325
...@@ -15,23 +15,59 @@ pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;...@@ -15,23 +15,59 @@ pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
1515
16const Allocator = mem.Allocator;16const Allocator = mem.Allocator;
1717
18usingnamespace if (comptime @hasDecl(c, "malloc_size"))
19 struct {
20 pub const supports_malloc_size = true;
21 pub const malloc_size = c.malloc_size;
22 }
23else if (comptime @hasDecl(c, "malloc_usable_size"))
24 struct {
25 pub const supports_malloc_size = true;
26 pub const malloc_size = c.malloc_usable_size;
27 }
28else
29 struct {
30 pub const supports_malloc_size = false;
31 };
32
18pub const c_allocator = &c_allocator_state;33pub const c_allocator = &c_allocator_state;
19var c_allocator_state = Allocator{34var c_allocator_state = Allocator{
20 .reallocFn = cRealloc,35 .allocFn = cAlloc,
21 .shrinkFn = cShrink,36 .resizeFn = cResize,
22};37};
2338
24fn cRealloc(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {39fn cAlloc(self: *Allocator, len: usize, ptr_align: u29, len_align: u29) Allocator.Error![]u8 {
25 assert(new_align <= @alignOf(c_longdouble));40 assert(ptr_align <= @alignOf(c_longdouble));
26 const old_ptr = if (old_mem.len == 0) null else @ptrCast(*c_void, old_mem.ptr);41 const ptr = @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory);
27 const buf = c.realloc(old_ptr, new_size) orelse return error.OutOfMemory;42 if (len_align == 0) {
28 return @ptrCast([*]u8, buf)[0..new_size];43 return ptr[0..len];
44 }
45 const full_len = init: {
46 if (supports_malloc_size) {
47 const s = malloc_size(ptr);
48 assert(s >= len);
49 break :init s;
50 }
51 break :init len;
52 };
53 return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)];
29}54}
3055
31fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {56fn cResize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Allocator.Error!usize {
32 const old_ptr = @ptrCast(*c_void, old_mem.ptr);57 if (new_len == 0) {
33 const buf = c.realloc(old_ptr, new_size) orelse return old_mem[0..new_size];58 c.free(buf.ptr);
34 return @ptrCast([*]u8, buf)[0..new_size];59 return 0;
60 }
61 if (new_len <= buf.len) {
62 return mem.alignAllocLen(buf.len, new_len, len_align);
63 }
64 if (supports_malloc_size) {
65 const full_len = malloc_size(buf.ptr);
66 if (new_len <= full_len) {
67 return mem.alignAllocLen(full_len, new_len, len_align);
68 }
69 }
70 return error.OutOfMemory;
35}71}
3672
37/// This allocator makes a syscall directly for every allocation and free.73/// This allocator makes a syscall directly for every allocation and free.
...@@ -44,19 +80,27 @@ else...@@ -44,19 +80,27 @@ else
44 &page_allocator_state;80 &page_allocator_state;
4581
46var page_allocator_state = Allocator{82var page_allocator_state = Allocator{
47 .reallocFn = PageAllocator.realloc,83 .allocFn = PageAllocator.alloc,
48 .shrinkFn = PageAllocator.shrink,84 .resizeFn = PageAllocator.resize,
49};85};
50var wasm_page_allocator_state = Allocator{86var wasm_page_allocator_state = Allocator{
51 .reallocFn = WasmPageAllocator.realloc,87 .allocFn = WasmPageAllocator.alloc,
52 .shrinkFn = WasmPageAllocator.shrink,88 .resizeFn = WasmPageAllocator.resize,
53};89};
5490
55pub const direct_allocator = @compileError("deprecated; use std.heap.page_allocator");91pub const direct_allocator = @compileError("deprecated; use std.heap.page_allocator");
5692
93/// Verifies that the adjusted length will still map to the full length
94pub fn alignPageAllocLen(full_len: usize, len: usize, len_align: u29) usize {
95 const aligned_len = mem.alignAllocLen(full_len, len, len_align);
96 assert(mem.alignForward(aligned_len, mem.page_size) == full_len);
97 return aligned_len;
98}
99
57const PageAllocator = struct {100const PageAllocator = struct {
58 fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {101 fn alloc(allocator: *Allocator, n: usize, alignment: u29, len_align: u29) error{OutOfMemory}![]u8 {
59 if (n == 0) return &[0]u8{};102 assert(n > 0);
103 const alignedLen = mem.alignForward(n, mem.page_size);
60104
61 if (builtin.os.tag == .windows) {105 if (builtin.os.tag == .windows) {
62 const w = os.windows;106 const w = os.windows;
...@@ -68,21 +112,21 @@ const PageAllocator = struct {...@@ -68,21 +112,21 @@ const PageAllocator = struct {
68 // see https://devblogs.microsoft.com/oldnewthing/?p=42223112 // see https://devblogs.microsoft.com/oldnewthing/?p=42223
69 const addr = w.VirtualAlloc(113 const addr = w.VirtualAlloc(
70 null,114 null,
71 n,115 alignedLen,
72 w.MEM_COMMIT | w.MEM_RESERVE,116 w.MEM_COMMIT | w.MEM_RESERVE,
73 w.PAGE_READWRITE,117 w.PAGE_READWRITE,
74 ) catch return error.OutOfMemory;118 ) catch return error.OutOfMemory;
75119
76 // If the allocation is sufficiently aligned, use it.120 // If the allocation is sufficiently aligned, use it.
77 if (@ptrToInt(addr) & (alignment - 1) == 0) {121 if (@ptrToInt(addr) & (alignment - 1) == 0) {
78 return @ptrCast([*]u8, addr)[0..n];122 return @ptrCast([*]u8, addr)[0..alignPageAllocLen(alignedLen, n, len_align)];
79 }123 }
80124
81 // If it wasn't, actually do an explicitely aligned allocation.125 // If it wasn't, actually do an explicitely aligned allocation.
82 w.VirtualFree(addr, 0, w.MEM_RELEASE);126 w.VirtualFree(addr, 0, w.MEM_RELEASE);
83 const alloc_size = n + alignment;127 const alloc_size = n + alignment - mem.page_size;
84128
85 const final_addr = while (true) {129 while (true) {
86 // Reserve a range of memory large enough to find a sufficiently130 // Reserve a range of memory large enough to find a sufficiently
87 // aligned address.131 // aligned address.
88 const reserved_addr = w.VirtualAlloc(132 const reserved_addr = w.VirtualAlloc(
...@@ -102,48 +146,49 @@ const PageAllocator = struct {...@@ -102,48 +146,49 @@ const PageAllocator = struct {
102 // until it succeeds.146 // until it succeeds.
103 const ptr = w.VirtualAlloc(147 const ptr = w.VirtualAlloc(
104 @intToPtr(*c_void, aligned_addr),148 @intToPtr(*c_void, aligned_addr),
105 n,149 alignedLen,
106 w.MEM_COMMIT | w.MEM_RESERVE,150 w.MEM_COMMIT | w.MEM_RESERVE,
107 w.PAGE_READWRITE,151 w.PAGE_READWRITE,
108 ) catch continue;152 ) catch continue;
109153
110 return @ptrCast([*]u8, ptr)[0..n];154 return @ptrCast([*]u8, ptr)[0..alignPageAllocLen(alignedLen, n, len_align)];
111 };155 }
112
113 return @ptrCast([*]u8, final_addr)[0..n];
114 }156 }
115157
116 const alloc_size = if (alignment <= mem.page_size) n else n + alignment;158 const maxDropLen = alignment - std.math.min(alignment, mem.page_size);
159 const allocLen = if (maxDropLen <= alignedLen - n) alignedLen else mem.alignForward(alignedLen + maxDropLen, mem.page_size);
117 const slice = os.mmap(160 const slice = os.mmap(
118 null,161 null,
119 mem.alignForward(alloc_size, mem.page_size),162 allocLen,
120 os.PROT_READ | os.PROT_WRITE,163 os.PROT_READ | os.PROT_WRITE,
121 os.MAP_PRIVATE | os.MAP_ANONYMOUS,164 os.MAP_PRIVATE | os.MAP_ANONYMOUS,
122 -1,165 -1,
123 0,166 0,
124 ) catch return error.OutOfMemory;167 ) catch return error.OutOfMemory;
125 if (alloc_size == n) return slice[0..n];168 assert(mem.isAligned(@ptrToInt(slice.ptr), mem.page_size));
126169
127 const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment);170 const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment);
128171
129 // Unmap the extra bytes that were only requested in order to guarantee172 // Unmap the extra bytes that were only requested in order to guarantee
130 // that the range of memory we were provided had a proper alignment in173 // that the range of memory we were provided had a proper alignment in
131 // it somewhere. The extra bytes could be at the beginning, or end, or both.174 // it somewhere. The extra bytes could be at the beginning, or end, or both.
132 const unused_start_len = aligned_addr - @ptrToInt(slice.ptr);175 const dropLen = aligned_addr - @ptrToInt(slice.ptr);
133 if (unused_start_len != 0) {176 if (dropLen != 0) {
134 os.munmap(slice[0..unused_start_len]);177 os.munmap(slice[0..dropLen]);
135 }178 }
136 const aligned_end_addr = mem.alignForward(aligned_addr + n, mem.page_size);179
137 const unused_end_len = @ptrToInt(slice.ptr) + slice.len - aligned_end_addr;180 // Unmap extra pages
138 if (unused_end_len != 0) {181 const alignedBufferLen = allocLen - dropLen;
139 os.munmap(@intToPtr([*]align(mem.page_size) u8, aligned_end_addr)[0..unused_end_len]);182 if (alignedBufferLen > alignedLen) {
183 os.munmap(@alignCast(mem.page_size, @intToPtr([*]u8, aligned_addr))[alignedLen..alignedBufferLen]);
140 }184 }
141185
142 return @intToPtr([*]u8, aligned_addr)[0..n];186 return @intToPtr([*]u8, aligned_addr)[0..alignPageAllocLen(alignedLen, n, len_align)];
143 }187 }
144188
145 fn shrink(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {189 fn resize(allocator: *Allocator, buf_unaligned: []u8, new_size: usize, len_align: u29) Allocator.Error!usize {
146 const old_mem = @alignCast(mem.page_size, old_mem_unaligned);190 const new_size_aligned = mem.alignForward(new_size, mem.page_size);
191
147 if (builtin.os.tag == .windows) {192 if (builtin.os.tag == .windows) {
148 const w = os.windows;193 const w = os.windows;
149 if (new_size == 0) {194 if (new_size == 0) {
...@@ -153,100 +198,45 @@ const PageAllocator = struct {...@@ -153,100 +198,45 @@ const PageAllocator = struct {
153 // is reserved in the initial allocation call to VirtualAlloc."198 // is reserved in the initial allocation call to VirtualAlloc."
154 // So we can only use MEM_RELEASE when actually releasing the199 // So we can only use MEM_RELEASE when actually releasing the
155 // whole allocation.200 // whole allocation.
156 w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE);201 w.VirtualFree(buf_unaligned.ptr, 0, w.MEM_RELEASE);
157 } else {202 return 0;
158 const base_addr = @ptrToInt(old_mem.ptr);203 }
159 const old_addr_end = base_addr + old_mem.len;204 if (new_size < buf_unaligned.len) {
160 const new_addr_end = base_addr + new_size;205 const base_addr = @ptrToInt(buf_unaligned.ptr);
161 const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size);206 const old_addr_end = base_addr + buf_unaligned.len;
162 if (old_addr_end > new_addr_end_rounded) {207 const new_addr_end = mem.alignForward(base_addr + new_size, mem.page_size);
208 if (old_addr_end > new_addr_end) {
163 // For shrinking that is not releasing, we will only209 // For shrinking that is not releasing, we will only
164 // decommit the pages not needed anymore.210 // decommit the pages not needed anymore.
165 w.VirtualFree(211 w.VirtualFree(
166 @intToPtr(*c_void, new_addr_end_rounded),212 @intToPtr(*c_void, new_addr_end),
167 old_addr_end - new_addr_end_rounded,213 old_addr_end - new_addr_end,
168 w.MEM_DECOMMIT,214 w.MEM_DECOMMIT,
169 );215 );
170 }216 }
217 return alignPageAllocLen(new_size_aligned, new_size, len_align);
171 }218 }
172 return old_mem[0..new_size];219 if (new_size == buf_unaligned.len) {
173 }220 return alignPageAllocLen(new_size_aligned, new_size, len_align);
174 const base_addr = @ptrToInt(old_mem.ptr);
175 const old_addr_end = base_addr + old_mem.len;
176 const new_addr_end = base_addr + new_size;
177 const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size);
178 if (old_addr_end > new_addr_end_rounded) {
179 const ptr = @intToPtr([*]align(mem.page_size) u8, new_addr_end_rounded);
180 os.munmap(ptr[0 .. old_addr_end - new_addr_end_rounded]);
181 }
182 return old_mem[0..new_size];
183 }
184
185 fn realloc(allocator: *Allocator, old_mem_unaligned: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
186 const old_mem = @alignCast(mem.page_size, old_mem_unaligned);
187 if (builtin.os.tag == .windows) {
188 if (old_mem.len == 0) {
189 return alloc(allocator, new_size, new_align);
190 }
191
192 if (new_size <= old_mem.len and new_align <= old_align) {
193 return shrink(allocator, old_mem, old_align, new_size, new_align);
194 }
195
196 const w = os.windows;
197 const base_addr = @ptrToInt(old_mem.ptr);
198
199 if (new_align > old_align and base_addr & (new_align - 1) != 0) {
200 // Current allocation doesn't satisfy the new alignment.
201 // For now we'll do a new one no matter what, but maybe
202 // there is something smarter to do instead.
203 const result = try alloc(allocator, new_size, new_align);
204 assert(old_mem.len != 0);
205 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
206 w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE);
207
208 return result;
209 }
210
211 const old_addr_end = base_addr + old_mem.len;
212 const old_addr_end_rounded = mem.alignForward(old_addr_end, mem.page_size);
213 const new_addr_end = base_addr + new_size;
214 const new_addr_end_rounded = mem.alignForward(new_addr_end, mem.page_size);
215 if (new_addr_end_rounded == old_addr_end_rounded) {
216 // The reallocation fits in the already allocated pages.
217 return @ptrCast([*]u8, old_mem.ptr)[0..new_size];
218 }221 }
219 assert(new_addr_end_rounded > old_addr_end_rounded);222 // new_size > buf_unaligned.len not implemented
223 return error.OutOfMemory;
224 }
220225
221 // We need to commit new pages.226 const buf_aligned_len = mem.alignForward(buf_unaligned.len, mem.page_size);
222 const additional_size = new_addr_end - old_addr_end_rounded;227 if (new_size_aligned == buf_aligned_len)
223 const realloc_addr = w.kernel32.VirtualAlloc(228 return alignPageAllocLen(new_size_aligned, new_size, len_align);
224 @intToPtr(*c_void, old_addr_end_rounded),
225 additional_size,
226 w.MEM_COMMIT | w.MEM_RESERVE,
227 w.PAGE_READWRITE,
228 ) orelse {
229 // Committing new pages at the end of the existing allocation
230 // failed, we need to try a new one.
231 const new_alloc_mem = try alloc(allocator, new_size, new_align);
232 @memcpy(new_alloc_mem.ptr, old_mem.ptr, old_mem.len);
233 w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE);
234
235 return new_alloc_mem;
236 };
237229
238 assert(@ptrToInt(realloc_addr) == old_addr_end_rounded);230 if (new_size_aligned < buf_aligned_len) {
239 return @ptrCast([*]u8, old_mem.ptr)[0..new_size];231 const ptr = @intToPtr([*]align(mem.page_size) u8, @ptrToInt(buf_unaligned.ptr) + new_size_aligned);
240 }232 os.munmap(ptr[0 .. buf_aligned_len - new_size_aligned]);
241 if (new_size <= old_mem.len and new_align <= old_align) {233 if (new_size_aligned == 0)
242 return shrink(allocator, old_mem, old_align, new_size, new_align);234 return 0;
235 return alignPageAllocLen(new_size_aligned, new_size, len_align);
243 }236 }
244 const result = try alloc(allocator, new_size, new_align);237
245 if (old_mem.len != 0) {238 // TODO: call mremap
246 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));239 return error.OutOfMemory;
247 os.munmap(old_mem);
248 }
249 return result;
250 }240 }
251};241};
252242
...@@ -299,7 +289,7 @@ const WasmPageAllocator = struct {...@@ -299,7 +289,7 @@ const WasmPageAllocator = struct {
299 // Revisit if this is settled: https://github.com/ziglang/zig/issues/3806289 // Revisit if this is settled: https://github.com/ziglang/zig/issues/3806
300 const not_found = std.math.maxInt(usize);290 const not_found = std.math.maxInt(usize);
301291
302 fn useRecycled(self: FreeBlock, num_pages: usize) usize {292 fn useRecycled(self: FreeBlock, num_pages: usize, alignment: u29) usize {
303 @setCold(true);293 @setCold(true);
304 for (self.data) |segment, i| {294 for (self.data) |segment, i| {
305 const spills_into_next = @bitCast(i128, segment) < 0;295 const spills_into_next = @bitCast(i128, segment) < 0;
...@@ -312,7 +302,8 @@ const WasmPageAllocator = struct {...@@ -312,7 +302,8 @@ const WasmPageAllocator = struct {
312 var count: usize = 0;302 var count: usize = 0;
313 while (j + count < self.totalPages() and self.getBit(j + count) == .free) {303 while (j + count < self.totalPages() and self.getBit(j + count) == .free) {
314 count += 1;304 count += 1;
315 if (count >= num_pages) {305 const addr = j * mem.page_size;
306 if (count >= num_pages and mem.isAligned(addr, alignment)) {
316 self.setBits(j, num_pages, .used);307 self.setBits(j, num_pages, .used);
317 return j;308 return j;
318 }309 }
...@@ -338,73 +329,72 @@ const WasmPageAllocator = struct {...@@ -338,73 +329,72 @@ const WasmPageAllocator = struct {
338 }329 }
339330
340 fn nPages(memsize: usize) usize {331 fn nPages(memsize: usize) usize {
341 return std.mem.alignForward(memsize, std.mem.page_size) / std.mem.page_size;332 return mem.alignForward(memsize, mem.page_size) / mem.page_size;
342 }333 }
343334
344 fn alloc(allocator: *Allocator, page_count: usize, alignment: u29) error{OutOfMemory}!usize {335 fn alloc(allocator: *Allocator, len: usize, alignment: u29, len_align: u29) error{OutOfMemory}![]u8 {
345 var idx = conventional.useRecycled(page_count);336 const page_count = nPages(len);
346 if (idx != FreeBlock.not_found) {337 const page_idx = try allocPages(page_count, alignment);
347 return idx;338 return @intToPtr([*]u8, page_idx * mem.page_size)[0..alignPageAllocLen(page_count * mem.page_size, len, len_align)];
339 }
340 fn allocPages(page_count: usize, alignment: u29) !usize {
341 {
342 const idx = conventional.useRecycled(page_count, alignment);
343 if (idx != FreeBlock.not_found) {
344 return idx;
345 }
348 }346 }
349347
350 idx = extended.useRecycled(page_count);348 const idx = extended.useRecycled(page_count, alignment);
351 if (idx != FreeBlock.not_found) {349 if (idx != FreeBlock.not_found) {
352 return idx + extendedOffset();350 return idx + extendedOffset();
353 }351 }
354352
355 const prev_page_count = @wasmMemoryGrow(0, @intCast(u32, page_count));353 const next_page_idx = @wasmMemorySize(0);
356 if (prev_page_count <= 0) {354 const next_page_addr = next_page_idx * mem.page_size;
355 const aligned_addr = mem.alignForward(next_page_addr, alignment);
356 const drop_page_count = @divExact(aligned_addr - next_page_addr, mem.page_size);
357 const result = @wasmMemoryGrow(0, @intCast(u32, drop_page_count + page_count));
358 if (result <= 0)
357 return error.OutOfMemory;359 return error.OutOfMemory;
360 assert(result == next_page_idx);
361 const aligned_page_idx = next_page_idx + drop_page_count;
362 if (drop_page_count > 0) {
363 freePages(next_page_idx, aligned_page_idx);
358 }364 }
359365 return @intCast(usize, aligned_page_idx);
360 return @intCast(usize, prev_page_count);
361 }366 }
362367
363 pub fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) Allocator.Error![]u8 {368 fn freePages(start: usize, end: usize) void {
364 if (new_align > std.mem.page_size) {369 if (start < extendedOffset()) {
365 return error.OutOfMemory;370 conventional.recycle(start, std.math.min(extendedOffset(), end) - start);
366 }371 }
367372 if (end > extendedOffset()) {
368 if (nPages(new_size) == nPages(old_mem.len)) {373 var new_end = end;
369 return old_mem.ptr[0..new_size];374 if (!extended.isInitialized()) {
370 } else if (new_size < old_mem.len) {375 // Steal the last page from the memory currently being recycled
371 return shrink(allocator, old_mem, old_align, new_size, new_align);376 // TODO: would it be better if we use the first page instead?
372 } else {377 new_end -= 1;
373 const page_idx = try alloc(allocator, nPages(new_size), new_align);378
374 const new_mem = @intToPtr([*]u8, page_idx * std.mem.page_size)[0..new_size];379 extended.data = @intToPtr([*]u128, new_end * mem.page_size)[0 .. mem.page_size / @sizeOf(u128)];
375 std.mem.copy(u8, new_mem, old_mem);380 // Since this is the first page being freed and we consume it, assume *nothing* is free.
376 _ = shrink(allocator, old_mem, old_align, 0, 0);381 mem.set(u128, extended.data, PageStatus.none_free);
377 return new_mem;382 }
383 const clamped_start = std.math.max(extendedOffset(), start);
384 extended.recycle(clamped_start - extendedOffset(), new_end - clamped_start);
378 }385 }
379 }386 }
380387
381 pub fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {388 fn resize(allocator: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!usize {
382 @setCold(true);389 const aligned_len = mem.alignForward(buf.len, mem.page_size);
383 const free_start = nPages(@ptrToInt(old_mem.ptr) + new_size);390 if (new_len > aligned_len) return error.OutOfMemory;
384 var free_end = nPages(@ptrToInt(old_mem.ptr) + old_mem.len);391 const current_n = nPages(aligned_len);
385392 const new_n = nPages(new_len);
386 if (free_end > free_start) {393 if (new_n != current_n) {
387 if (free_start < extendedOffset()) {394 const base = nPages(@ptrToInt(buf.ptr));
388 const clamped_end = std.math.min(extendedOffset(), free_end);395 freePages(base + new_n, base + current_n);
389 conventional.recycle(free_start, clamped_end - free_start);
390 }
391
392 if (free_end > extendedOffset()) {
393 if (!extended.isInitialized()) {
394 // Steal the last page from the memory currently being recycled
395 // TODO: would it be better if we use the first page instead?
396 free_end -= 1;
397
398 extended.data = @intToPtr([*]u128, free_end * std.mem.page_size)[0 .. std.mem.page_size / @sizeOf(u128)];
399 // Since this is the first page being freed and we consume it, assume *nothing* is free.
400 std.mem.set(u128, extended.data, PageStatus.none_free);
401 }
402 const clamped_start = std.math.max(extendedOffset(), free_start);
403 extended.recycle(clamped_start - extendedOffset(), free_end - clamped_start);
404 }
405 }396 }
406397 return if (new_len == 0) 0 else alignPageAllocLen(new_n * mem.page_size, new_len, len_align);
407 return old_mem[0..new_size];
408 }398 }
409};399};
410400
...@@ -418,8 +408,8 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -418,8 +408,8 @@ pub const HeapAllocator = switch (builtin.os.tag) {
418 pub fn init() HeapAllocator {408 pub fn init() HeapAllocator {
419 return HeapAllocator{409 return HeapAllocator{
420 .allocator = Allocator{410 .allocator = Allocator{
421 .reallocFn = realloc,411 .allocFn = alloc,
422 .shrinkFn = shrink,412 .resizeFn = resize,
423 },413 },
424 .heap_handle = null,414 .heap_handle = null,
425 };415 };
...@@ -431,11 +421,14 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -431,11 +421,14 @@ pub const HeapAllocator = switch (builtin.os.tag) {
431 }421 }
432 }422 }
433423
434 fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {424 fn getRecordPtr(buf: []u8) *align(1) usize {
425 return @intToPtr(*align(1) usize, @ptrToInt(buf.ptr) + buf.len);
426 }
427
428 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
435 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);429 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
436 if (n == 0) return &[0]u8{};
437430
438 const amt = n + alignment + @sizeOf(usize);431 const amt = n + ptr_align - 1 + @sizeOf(usize);
439 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);432 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);
440 const heap_handle = optional_heap_handle orelse blk: {433 const heap_handle = optional_heap_handle orelse blk: {
441 const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0;434 const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0;
...@@ -446,66 +439,60 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -446,66 +439,60 @@ pub const HeapAllocator = switch (builtin.os.tag) {
446 };439 };
447 const ptr = os.windows.kernel32.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;440 const ptr = os.windows.kernel32.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
448 const root_addr = @ptrToInt(ptr);441 const root_addr = @ptrToInt(ptr);
449 const adjusted_addr = mem.alignForward(root_addr, alignment);442 const aligned_addr = mem.alignForward(root_addr, ptr_align);
450 const record_addr = adjusted_addr + n;443 const return_len = init: {
451 @intToPtr(*align(1) usize, record_addr).* = root_addr;444 if (len_align == 0) break :init n;
452 return @intToPtr([*]u8, adjusted_addr)[0..n];445 const full_len = os.windows.kernel32.HeapSize(heap_handle, 0, ptr);
453 }446 assert(full_len != std.math.maxInt(usize));
454447 assert(full_len >= amt);
455 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {448 break :init mem.alignBackwardAnyAlign(full_len - (aligned_addr - root_addr), len_align);
456 return realloc(allocator, old_mem, old_align, new_size, new_align) catch {
457 const old_adjusted_addr = @ptrToInt(old_mem.ptr);
458 const old_record_addr = old_adjusted_addr + old_mem.len;
459 const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
460 const old_ptr = @intToPtr(*c_void, root_addr);
461 const new_record_addr = old_record_addr - new_size + old_mem.len;
462 @intToPtr(*align(1) usize, new_record_addr).* = root_addr;
463 return old_mem[0..new_size];
464 };449 };
450 const buf = @intToPtr([*]u8, aligned_addr)[0..return_len];
451 getRecordPtr(buf).* = root_addr;
452 return buf;
465 }453 }
466454
467 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {455 fn resize(allocator: *Allocator, buf: []u8, new_size: usize, len_align: u29) error{OutOfMemory}!usize {
468 if (old_mem.len == 0) return alloc(allocator, new_size, new_align);
469
470 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);456 const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
471 const old_adjusted_addr = @ptrToInt(old_mem.ptr);
472 const old_record_addr = old_adjusted_addr + old_mem.len;
473 const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
474 const old_ptr = @intToPtr(*c_void, root_addr);
475
476 if (new_size == 0) {457 if (new_size == 0) {
477 os.windows.HeapFree(self.heap_handle.?, 0, old_ptr);458 os.windows.HeapFree(self.heap_handle.?, 0, @intToPtr(*c_void, getRecordPtr(buf).*));
478 return old_mem[0..0];459 return 0;
479 }460 }
480461
481 const amt = new_size + new_align + @sizeOf(usize);462 const root_addr = getRecordPtr(buf).*;
463 const align_offset = @ptrToInt(buf.ptr) - root_addr;
464 const amt = align_offset + new_size + @sizeOf(usize);
482 const new_ptr = os.windows.kernel32.HeapReAlloc(465 const new_ptr = os.windows.kernel32.HeapReAlloc(
483 self.heap_handle.?,466 self.heap_handle.?,
484 0,467 os.windows.HEAP_REALLOC_IN_PLACE_ONLY,
485 old_ptr,468 @intToPtr(*c_void, root_addr),
486 amt,469 amt,
487 ) orelse return error.OutOfMemory;470 ) orelse return error.OutOfMemory;
488 const offset = old_adjusted_addr - root_addr;471 assert(new_ptr == @intToPtr(*c_void, root_addr));
489 const new_root_addr = @ptrToInt(new_ptr);472 const return_len = init: {
490 var new_adjusted_addr = new_root_addr + offset;473 if (len_align == 0) break :init new_size;
491 const offset_is_valid = new_adjusted_addr + new_size + @sizeOf(usize) <= new_root_addr + amt;474 const full_len = os.windows.kernel32.HeapSize(self.heap_handle.?, 0, new_ptr);
492 const offset_is_aligned = new_adjusted_addr % new_align == 0;475 assert(full_len != std.math.maxInt(usize));
493 if (!offset_is_valid or !offset_is_aligned) {476 assert(full_len >= amt);
494 // If HeapReAlloc didn't happen to move the memory to the new alignment,477 break :init mem.alignBackwardAnyAlign(full_len - align_offset, len_align);
495 // or the memory starting at the old offset would be outside of the new allocation,478 };
496 // then we need to copy the memory to a valid aligned address and use that479 getRecordPtr(buf.ptr[0..return_len]).* = root_addr;
497 const new_aligned_addr = mem.alignForward(new_root_addr, new_align);480 return return_len;
498 @memcpy(@intToPtr([*]u8, new_aligned_addr), @intToPtr([*]u8, new_adjusted_addr), std.math.min(old_mem.len, new_size));
499 new_adjusted_addr = new_aligned_addr;
500 }
501 const new_record_addr = new_adjusted_addr + new_size;
502 @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr;
503 return @intToPtr([*]u8, new_adjusted_addr)[0..new_size];
504 }481 }
505 },482 },
506 else => @compileError("Unsupported OS"),483 else => @compileError("Unsupported OS"),
507};484};
508485
486fn sliceContainsPtr(container: []u8, ptr: [*]u8) bool {
487 return @ptrToInt(ptr) >= @ptrToInt(container.ptr) and
488 @ptrToInt(ptr) < (@ptrToInt(container.ptr) + container.len);
489}
490
491fn sliceContainsSlice(container: []u8, slice: []u8) bool {
492 return @ptrToInt(slice.ptr) >= @ptrToInt(container.ptr) and
493 (@ptrToInt(slice.ptr) + slice.len) <= (@ptrToInt(container.ptr) + container.len);
494}
495
509pub const FixedBufferAllocator = struct {496pub const FixedBufferAllocator = struct {
510 allocator: Allocator,497 allocator: Allocator,
511 end_index: usize,498 end_index: usize,
...@@ -514,19 +501,33 @@ pub const FixedBufferAllocator = struct {...@@ -514,19 +501,33 @@ pub const FixedBufferAllocator = struct {
514 pub fn init(buffer: []u8) FixedBufferAllocator {501 pub fn init(buffer: []u8) FixedBufferAllocator {
515 return FixedBufferAllocator{502 return FixedBufferAllocator{
516 .allocator = Allocator{503 .allocator = Allocator{
517 .reallocFn = realloc,504 .allocFn = alloc,
518 .shrinkFn = shrink,505 .resizeFn = resize,
519 },506 },
520 .buffer = buffer,507 .buffer = buffer,
521 .end_index = 0,508 .end_index = 0,
522 };509 };
523 }510 }
524511
525 fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {512 pub fn ownsPtr(self: *FixedBufferAllocator, ptr: [*]u8) bool {
513 return sliceContainsPtr(self.buffer, ptr);
514 }
515
516 pub fn ownsSlice(self: *FixedBufferAllocator, slice: []u8) bool {
517 return sliceContainsSlice(self.buffer, slice);
518 }
519
520 /// NOTE: this will not work in all cases, if the last allocation had an adjusted_index
521 /// then we won't be able to determine what the last allocation was. This is because
522 /// the alignForward operation done in alloc is not reverisible.
523 pub fn isLastAllocation(self: *FixedBufferAllocator, buf: []u8) bool {
524 return buf.ptr + buf.len == self.buffer.ptr + self.end_index;
525 }
526
527 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 {
526 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);528 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
527 const addr = @ptrToInt(self.buffer.ptr) + self.end_index;529 const aligned_addr = mem.alignForward(@ptrToInt(self.buffer.ptr) + self.end_index, ptr_align);
528 const adjusted_addr = mem.alignForward(addr, alignment);530 const adjusted_index = aligned_addr - @ptrToInt(self.buffer.ptr);
529 const adjusted_index = self.end_index + (adjusted_addr - addr);
530 const new_end_index = adjusted_index + n;531 const new_end_index = adjusted_index + n;
531 if (new_end_index > self.buffer.len) {532 if (new_end_index > self.buffer.len) {
532 return error.OutOfMemory;533 return error.OutOfMemory;
...@@ -537,30 +538,28 @@ pub const FixedBufferAllocator = struct {...@@ -537,30 +538,28 @@ pub const FixedBufferAllocator = struct {
537 return result;538 return result;
538 }539 }
539540
540 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {541 fn resize(allocator: *Allocator, buf: []u8, new_size: usize, len_align: u29) Allocator.Error!usize {
541 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);542 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
542 assert(old_mem.len <= self.end_index);543 assert(self.ownsSlice(buf)); // sanity check
543 if (old_mem.ptr == self.buffer.ptr + self.end_index - old_mem.len and544
544 mem.alignForward(@ptrToInt(old_mem.ptr), new_align) == @ptrToInt(old_mem.ptr))545 if (!self.isLastAllocation(buf)) {
545 {546 if (new_size > buf.len)
546 const start_index = self.end_index - old_mem.len;547 return error.OutOfMemory;
547 const new_end_index = start_index + new_size;548 return if (new_size == 0) 0 else mem.alignAllocLen(buf.len, new_size, len_align);
548 if (new_end_index > self.buffer.len) return error.OutOfMemory;549 }
549 const result = self.buffer[start_index..new_end_index];550
550 self.end_index = new_end_index;551 if (new_size <= buf.len) {
551 return result;552 const sub = buf.len - new_size;
552 } else if (new_size <= old_mem.len and new_align <= old_align) {553 self.end_index -= sub;
553 // We can't do anything with the memory, so tell the client to keep it.554 return if (new_size == 0) 0 else mem.alignAllocLen(buf.len - sub, new_size, len_align);
554 return error.OutOfMemory;
555 } else {
556 const result = try alloc(allocator, new_size, new_align);
557 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
558 return result;
559 }555 }
560 }
561556
562 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {557 const add = new_size - buf.len;
563 return old_mem[0..new_size];558 if (add + self.end_index > self.buffer.len) {
559 return error.OutOfMemory;
560 }
561 self.end_index += add;
562 return new_size;
564 }563 }
565564
566 pub fn reset(self: *FixedBufferAllocator) void {565 pub fn reset(self: *FixedBufferAllocator) void {
...@@ -581,20 +580,20 @@ pub const ThreadSafeFixedBufferAllocator = blk: {...@@ -581,20 +580,20 @@ pub const ThreadSafeFixedBufferAllocator = blk: {
581 pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator {580 pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator {
582 return ThreadSafeFixedBufferAllocator{581 return ThreadSafeFixedBufferAllocator{
583 .allocator = Allocator{582 .allocator = Allocator{
584 .reallocFn = realloc,583 .allocFn = alloc,
585 .shrinkFn = shrink,584 .resizeFn = Allocator.noResize,
586 },585 },
587 .buffer = buffer,586 .buffer = buffer,
588 .end_index = 0,587 .end_index = 0,
589 };588 };
590 }589 }
591590
592 fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {591 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 {
593 const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);592 const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
594 var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);593 var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
595 while (true) {594 while (true) {
596 const addr = @ptrToInt(self.buffer.ptr) + end_index;595 const addr = @ptrToInt(self.buffer.ptr) + end_index;
597 const adjusted_addr = mem.alignForward(addr, alignment);596 const adjusted_addr = mem.alignForward(addr, ptr_align);
598 const adjusted_index = end_index + (adjusted_addr - addr);597 const adjusted_index = end_index + (adjusted_addr - addr);
599 const new_end_index = adjusted_index + n;598 const new_end_index = adjusted_index + n;
600 if (new_end_index > self.buffer.len) {599 if (new_end_index > self.buffer.len) {
...@@ -604,21 +603,6 @@ pub const ThreadSafeFixedBufferAllocator = blk: {...@@ -604,21 +603,6 @@ pub const ThreadSafeFixedBufferAllocator = blk: {
604 }603 }
605 }604 }
606605
607 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
608 if (new_size <= old_mem.len and new_align <= old_align) {
609 // We can't do anything useful with the memory, tell the client to keep it.
610 return error.OutOfMemory;
611 } else {
612 const result = try alloc(allocator, new_size, new_align);
613 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
614 return result;
615 }
616 }
617
618 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
619 return old_mem[0..new_size];
620 }
621
622 pub fn reset(self: *ThreadSafeFixedBufferAllocator) void {606 pub fn reset(self: *ThreadSafeFixedBufferAllocator) void {
623 self.end_index = 0;607 self.end_index = 0;
624 }608 }
...@@ -632,8 +616,8 @@ pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) Stack...@@ -632,8 +616,8 @@ pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) Stack
632 .fallback_allocator = fallback_allocator,616 .fallback_allocator = fallback_allocator,
633 .fixed_buffer_allocator = undefined,617 .fixed_buffer_allocator = undefined,
634 .allocator = Allocator{618 .allocator = Allocator{
635 .reallocFn = StackFallbackAllocator(size).realloc,619 .allocFn = StackFallbackAllocator(size).realloc,
636 .shrinkFn = StackFallbackAllocator(size).shrink,620 .resizeFn = StackFallbackAllocator(size).resize,
637 },621 },
638 };622 };
639}623}
...@@ -652,58 +636,19 @@ pub fn StackFallbackAllocator(comptime size: usize) type {...@@ -652,58 +636,19 @@ pub fn StackFallbackAllocator(comptime size: usize) type {
652 return &self.allocator;636 return &self.allocator;
653 }637 }
654638
655 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {639 fn alloc(allocator: *Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![*]u8 {
656 const self = @fieldParentPtr(Self, "allocator", allocator);640 const self = @fieldParentPtr(Self, "allocator", allocator);
657 const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and641 return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, ptr_align) catch
658 @ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len;642 return fallback_allocator.alloc(len, ptr_align);
659 if (in_buffer) {
660 return FixedBufferAllocator.realloc(
661 &self.fixed_buffer_allocator.allocator,
662 old_mem,
663 old_align,
664 new_size,
665 new_align,
666 ) catch {
667 const result = try self.fallback_allocator.reallocFn(
668 self.fallback_allocator,
669 &[0]u8{},
670 undefined,
671 new_size,
672 new_align,
673 );
674 mem.copy(u8, result, old_mem);
675 return result;
676 };
677 }
678 return self.fallback_allocator.reallocFn(
679 self.fallback_allocator,
680 old_mem,
681 old_align,
682 new_size,
683 new_align,
684 );
685 }643 }
686644
687 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {645 fn resize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!void {
688 const self = @fieldParentPtr(Self, "allocator", allocator);646 const self = @fieldParentPtr(Self, "allocator", allocator);
689 const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and647 if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
690 @ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len;648 try self.fixed_buffer_allocator.callResizeFn(buf, new_len);
691 if (in_buffer) {649 } else {
692 return FixedBufferAllocator.shrink(650 try self.fallback_allocator.callResizeFn(buf, new_len);
693 &self.fixed_buffer_allocator.allocator,
694 old_mem,
695 old_align,
696 new_size,
697 new_align,
698 );
699 }651 }
700 return self.fallback_allocator.shrinkFn(
701 self.fallback_allocator,
702 old_mem,
703 old_align,
704 new_size,
705 new_align,
706 );
707 }652 }
708 };653 };
709}654}
...@@ -718,8 +663,8 @@ test "c_allocator" {...@@ -718,8 +663,8 @@ test "c_allocator" {
718663
719test "WasmPageAllocator internals" {664test "WasmPageAllocator internals" {
720 if (comptime std.Target.current.isWasm()) {665 if (comptime std.Target.current.isWasm()) {
721 const conventional_memsize = WasmPageAllocator.conventional.totalPages() * std.mem.page_size;666 const conventional_memsize = WasmPageAllocator.conventional.totalPages() * mem.page_size;
722 const initial = try page_allocator.alloc(u8, std.mem.page_size);667 const initial = try page_allocator.alloc(u8, mem.page_size);
723 std.debug.assert(@ptrToInt(initial.ptr) < conventional_memsize); // If this isn't conventional, the rest of these tests don't make sense. Also we have a serious memory leak in the test suite.668 std.debug.assert(@ptrToInt(initial.ptr) < conventional_memsize); // If this isn't conventional, the rest of these tests don't make sense. Also we have a serious memory leak in the test suite.
724669
725 var inplace = try page_allocator.realloc(initial, 1);670 var inplace = try page_allocator.realloc(initial, 1);
...@@ -772,6 +717,11 @@ test "PageAllocator" {...@@ -772,6 +717,11 @@ test "PageAllocator" {
772 slice[127] = 0x34;717 slice[127] = 0x34;
773 allocator.free(slice);718 allocator.free(slice);
774 }719 }
720 {
721 var buf = try allocator.alloc(u8, mem.page_size + 1);
722 defer allocator.free(buf);
723 buf = try allocator.realloc(buf, 1); // shrink past the page boundary
724 }
775}725}
776726
777test "HeapAllocator" {727test "HeapAllocator" {
...@@ -799,7 +749,7 @@ test "ArenaAllocator" {...@@ -799,7 +749,7 @@ test "ArenaAllocator" {
799749
800var test_fixed_buffer_allocator_memory: [800000 * @sizeOf(u64)]u8 = undefined;750var test_fixed_buffer_allocator_memory: [800000 * @sizeOf(u64)]u8 = undefined;
801test "FixedBufferAllocator" {751test "FixedBufferAllocator" {
802 var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);752 var fixed_buffer_allocator = mem.validationWrap(FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]));
803753
804 try testAllocator(&fixed_buffer_allocator.allocator);754 try testAllocator(&fixed_buffer_allocator.allocator);
805 try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);755 try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
...@@ -865,7 +815,10 @@ test "ThreadSafeFixedBufferAllocator" {...@@ -865,7 +815,10 @@ test "ThreadSafeFixedBufferAllocator" {
865 try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator);815 try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator);
866}816}
867817
868fn testAllocator(allocator: *mem.Allocator) !void {818pub fn testAllocator(base_allocator: *mem.Allocator) !void {
819 var validationAllocator = mem.validationWrap(base_allocator);
820 const allocator = &validationAllocator.allocator;
821
869 var slice = try allocator.alloc(*i32, 100);822 var slice = try allocator.alloc(*i32, 100);
870 testing.expect(slice.len == 100);823 testing.expect(slice.len == 100);
871 for (slice) |*item, i| {824 for (slice) |*item, i| {
...@@ -893,7 +846,10 @@ fn testAllocator(allocator: *mem.Allocator) !void {...@@ -893,7 +846,10 @@ fn testAllocator(allocator: *mem.Allocator) !void {
893 allocator.free(slice);846 allocator.free(slice);
894}847}
895848
896fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !void {849pub fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void {
850 var validationAllocator = mem.validationWrap(base_allocator);
851 const allocator = &validationAllocator.allocator;
852
897 // initial853 // initial
898 var slice = try allocator.alignedAlloc(u8, alignment, 10);854 var slice = try allocator.alignedAlloc(u8, alignment, 10);
899 testing.expect(slice.len == 10);855 testing.expect(slice.len == 10);
...@@ -917,7 +873,10 @@ fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !voi...@@ -917,7 +873,10 @@ fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !voi
917 testing.expect(slice.len == 0);873 testing.expect(slice.len == 0);
918}874}
919875
920fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void {876pub fn testAllocatorLargeAlignment(base_allocator: *mem.Allocator) mem.Allocator.Error!void {
877 var validationAllocator = mem.validationWrap(base_allocator);
878 const allocator = &validationAllocator.allocator;
879
921 //Maybe a platform's page_size is actually the same as or880 //Maybe a platform's page_size is actually the same as or
922 // very near usize?881 // very near usize?
923 if (mem.page_size << 2 > maxInt(usize)) return;882 if (mem.page_size << 2 > maxInt(usize)) return;
...@@ -946,7 +905,10 @@ fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!vo...@@ -946,7 +905,10 @@ fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!vo
946 allocator.free(slice);905 allocator.free(slice);
947}906}
948907
949fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!void {908pub fn testAllocatorAlignedShrink(base_allocator: *mem.Allocator) mem.Allocator.Error!void {
909 var validationAllocator = mem.validationWrap(base_allocator);
910 const allocator = &validationAllocator.allocator;
911
950 var debug_buffer: [1000]u8 = undefined;912 var debug_buffer: [1000]u8 = undefined;
951 const debug_allocator = &FixedBufferAllocator.init(&debug_buffer).allocator;913 const debug_allocator = &FixedBufferAllocator.init(&debug_buffer).allocator;
952914
lib/std/heap/arena_allocator.zig+8-24
...@@ -20,8 +20,8 @@ pub const ArenaAllocator = struct {...@@ -20,8 +20,8 @@ pub const ArenaAllocator = struct {
20 pub fn promote(self: State, child_allocator: *Allocator) ArenaAllocator {20 pub fn promote(self: State, child_allocator: *Allocator) ArenaAllocator {
21 return .{21 return .{
22 .allocator = Allocator{22 .allocator = Allocator{
23 .reallocFn = realloc,23 .allocFn = alloc,
24 .shrinkFn = shrink,24 .resizeFn = Allocator.noResize,
25 },25 },
26 .child_allocator = child_allocator,26 .child_allocator = child_allocator,
27 .state = self,27 .state = self,
...@@ -49,9 +49,8 @@ pub const ArenaAllocator = struct {...@@ -49,9 +49,8 @@ pub const ArenaAllocator = struct {
49 const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16);49 const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16);
50 const big_enough_len = prev_len + actual_min_size;50 const big_enough_len = prev_len + actual_min_size;
51 const len = big_enough_len + big_enough_len / 2;51 const len = big_enough_len + big_enough_len / 2;
52 const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len);52 const buf = try self.child_allocator.callAllocFn(len, @alignOf(BufNode), 1);
53 const buf_node_slice = mem.bytesAsSlice(BufNode, buf[0..@sizeOf(BufNode)]);53 const buf_node = @ptrCast(*BufNode, @alignCast(@alignOf(BufNode), buf.ptr));
54 const buf_node = &buf_node_slice[0];
55 buf_node.* = BufNode{54 buf_node.* = BufNode{
56 .data = buf,55 .data = buf,
57 .next = null,56 .next = null,
...@@ -61,18 +60,18 @@ pub const ArenaAllocator = struct {...@@ -61,18 +60,18 @@ pub const ArenaAllocator = struct {
61 return buf_node;60 return buf_node;
62 }61 }
6362
64 fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {63 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 {
65 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);64 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
6665
67 var cur_node = if (self.state.buffer_list.first) |first_node| first_node else try self.createNode(0, n + alignment);66 var cur_node = if (self.state.buffer_list.first) |first_node| first_node else try self.createNode(0, n + ptr_align);
68 while (true) {67 while (true) {
69 const cur_buf = cur_node.data[@sizeOf(BufNode)..];68 const cur_buf = cur_node.data[@sizeOf(BufNode)..];
70 const addr = @ptrToInt(cur_buf.ptr) + self.state.end_index;69 const addr = @ptrToInt(cur_buf.ptr) + self.state.end_index;
71 const adjusted_addr = mem.alignForward(addr, alignment);70 const adjusted_addr = mem.alignForward(addr, ptr_align);
72 const adjusted_index = self.state.end_index + (adjusted_addr - addr);71 const adjusted_index = self.state.end_index + (adjusted_addr - addr);
73 const new_end_index = adjusted_index + n;72 const new_end_index = adjusted_index + n;
74 if (new_end_index > cur_buf.len) {73 if (new_end_index > cur_buf.len) {
75 cur_node = try self.createNode(cur_buf.len, n + alignment);74 cur_node = try self.createNode(cur_buf.len, n + ptr_align);
76 continue;75 continue;
77 }76 }
78 const result = cur_buf[adjusted_index..new_end_index];77 const result = cur_buf[adjusted_index..new_end_index];
...@@ -80,19 +79,4 @@ pub const ArenaAllocator = struct {...@@ -80,19 +79,4 @@ pub const ArenaAllocator = struct {
80 return result;79 return result;
81 }80 }
82 }81 }
83
84 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
85 if (new_size <= old_mem.len and new_align <= new_size) {
86 // We can't do anything with the memory, so tell the client to keep it.
87 return error.OutOfMemory;
88 } else {
89 const result = try alloc(allocator, new_size, new_align);
90 @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
91 return result;
92 }
93 }
94
95 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
96 return old_mem[0..new_size];
97 }
98};82};
lib/std/heap/logging_allocator.zig+38-25
...@@ -15,62 +15,75 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {...@@ -15,62 +15,75 @@ pub fn LoggingAllocator(comptime OutStreamType: type) type {
15 pub fn init(parent_allocator: *Allocator, out_stream: OutStreamType) Self {15 pub fn init(parent_allocator: *Allocator, out_stream: OutStreamType) Self {
16 return Self{16 return Self{
17 .allocator = Allocator{17 .allocator = Allocator{
18 .reallocFn = realloc,18 .allocFn = alloc,
19 .shrinkFn = shrink,19 .resizeFn = resize,
20 },20 },
21 .parent_allocator = parent_allocator,21 .parent_allocator = parent_allocator,
22 .out_stream = out_stream,22 .out_stream = out_stream,
23 };23 };
24 }24 }
2525
26 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {26 fn alloc(allocator: *Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
27 const self = @fieldParentPtr(Self, "allocator", allocator);27 const self = @fieldParentPtr(Self, "allocator", allocator);
28 if (old_mem.len == 0) {28 self.out_stream.print("alloc : {}", .{len}) catch {};
29 self.out_stream.print("allocation of {} ", .{new_size}) catch {};29 const result = self.parent_allocator.callAllocFn(len, ptr_align, len_align);
30 } else {
31 self.out_stream.print("resize from {} to {} ", .{ old_mem.len, new_size }) catch {};
32 }
33 const result = self.parent_allocator.reallocFn(self.parent_allocator, old_mem, old_align, new_size, new_align);
34 if (result) |buff| {30 if (result) |buff| {
35 self.out_stream.print("success!\n", .{}) catch {};31 self.out_stream.print(" success!\n", .{}) catch {};
36 } else |err| {32 } else |err| {
37 self.out_stream.print("failure!\n", .{}) catch {};33 self.out_stream.print(" failure!\n", .{}) catch {};
38 }34 }
39 return result;35 return result;
40 }36 }
4137
42 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {38 fn resize(allocator: *Allocator, buf: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!usize {
43 const self = @fieldParentPtr(Self, "allocator", allocator);39 const self = @fieldParentPtr(Self, "allocator", allocator);
44 const result = self.parent_allocator.shrinkFn(self.parent_allocator, old_mem, old_align, new_size, new_align);40 if (new_len == 0) {
45 if (new_size == 0) {41 self.out_stream.print("free : {}\n", .{buf.len}) catch {};
46 self.out_stream.print("free of {} bytes success!\n", .{old_mem.len}) catch {};42 } else if (new_len <= buf.len) {
43 self.out_stream.print("shrink: {} to {}\n", .{ buf.len, new_len }) catch {};
47 } else {44 } else {
48 self.out_stream.print("shrink from {} bytes to {} bytes success!\n", .{ old_mem.len, new_size }) catch {};45 self.out_stream.print("expand: {} to {}", .{ buf.len, new_len }) catch {};
46 }
47 if (self.parent_allocator.callResizeFn(buf, new_len, len_align)) |resized_len| {
48 if (new_len > buf.len) {
49 self.out_stream.print(" success!\n", .{}) catch {};
50 }
51 return resized_len;
52 } else |e| {
53 std.debug.assert(new_len > buf.len);
54 self.out_stream.print(" failure!\n", .{}) catch {};
55 return e;
49 }56 }
50 return result;
51 }57 }
52 };58 };
53}59}
5460
55pub fn loggingAllocator(61pub fn loggingAllocator(
56 parent_allocator: *Allocator,62 parent_allocator: *Allocator,
57 out_stream: var,63 out_stream: anytype,
58) LoggingAllocator(@TypeOf(out_stream)) {64) LoggingAllocator(@TypeOf(out_stream)) {
59 return LoggingAllocator(@TypeOf(out_stream)).init(parent_allocator, out_stream);65 return LoggingAllocator(@TypeOf(out_stream)).init(parent_allocator, out_stream);
60}66}
6167
62test "LoggingAllocator" {68test "LoggingAllocator" {
63 var buf: [255]u8 = undefined;69 var log_buf: [255]u8 = undefined;
64 var fbs = std.io.fixedBufferStream(&buf);70 var fbs = std.io.fixedBufferStream(&log_buf);
6571
66 const allocator = &loggingAllocator(std.testing.allocator, fbs.outStream()).allocator;72 var allocator_buf: [10]u8 = undefined;
73 var fixedBufferAllocator = std.mem.validationWrap(std.heap.FixedBufferAllocator.init(&allocator_buf));
74 const allocator = &loggingAllocator(&fixedBufferAllocator.allocator, fbs.outStream()).allocator;
6775
68 const ptr = try allocator.alloc(u8, 10);76 var a = try allocator.alloc(u8, 10);
69 allocator.free(ptr);77 a.len = allocator.shrinkBytes(a, 5, 0);
78 std.debug.assert(a.len == 5);
79 std.testing.expectError(error.OutOfMemory, allocator.callResizeFn(a, 20, 0));
80 allocator.free(a);
7081
71 std.testing.expectEqualSlices(u8,82 std.testing.expectEqualSlices(u8,
72 \\allocation of 10 success!83 \\alloc : 10 success!
73 \\free of 10 bytes success!84 \\shrink: 10 to 5
85 \\expand: 5 to 20 failure!
86 \\free : 5
74 \\87 \\
75 , fbs.getWritten());88 , fbs.getWritten());
76}89}
lib/std/http/headers.zig+83-87
...@@ -27,7 +27,6 @@ fn never_index_default(name: []const u8) bool {...@@ -27,7 +27,6 @@ fn never_index_default(name: []const u8) bool {
27}27}
2828
29const HeaderEntry = struct {29const HeaderEntry = struct {
30 allocator: *Allocator,
31 name: []const u8,30 name: []const u8,
32 value: []u8,31 value: []u8,
33 never_index: bool,32 never_index: bool,
...@@ -36,23 +35,22 @@ const HeaderEntry = struct {...@@ -36,23 +35,22 @@ const HeaderEntry = struct {
3635
37 fn init(allocator: *Allocator, name: []const u8, value: []const u8, never_index: ?bool) !Self {36 fn init(allocator: *Allocator, name: []const u8, value: []const u8, never_index: ?bool) !Self {
38 return Self{37 return Self{
39 .allocator = allocator,
40 .name = name, // takes reference38 .name = name, // takes reference
41 .value = try mem.dupe(allocator, u8, value),39 .value = try allocator.dupe(u8, value),
42 .never_index = never_index orelse never_index_default(name),40 .never_index = never_index orelse never_index_default(name),
43 };41 };
44 }42 }
4543
46 fn deinit(self: Self) void {44 fn deinit(self: Self, allocator: *Allocator) void {
47 self.allocator.free(self.value);45 allocator.free(self.value);
48 }46 }
4947
50 pub fn modify(self: *Self, value: []const u8, never_index: ?bool) !void {48 pub fn modify(self: *Self, allocator: *Allocator, value: []const u8, never_index: ?bool) !void {
51 const old_len = self.value.len;49 const old_len = self.value.len;
52 if (value.len > old_len) {50 if (value.len > old_len) {
53 self.value = try self.allocator.realloc(self.value, value.len);51 self.value = try allocator.realloc(self.value, value.len);
54 } else if (value.len < old_len) {52 } else if (value.len < old_len) {
55 self.value = self.allocator.shrink(self.value, value.len);53 self.value = allocator.shrink(self.value, value.len);
56 }54 }
57 mem.copy(u8, self.value, value);55 mem.copy(u8, self.value, value);
58 self.never_index = never_index orelse never_index_default(self.name);56 self.never_index = never_index orelse never_index_default(self.name);
...@@ -85,22 +83,22 @@ const HeaderEntry = struct {...@@ -85,22 +83,22 @@ const HeaderEntry = struct {
8583
86test "HeaderEntry" {84test "HeaderEntry" {
87 var e = try HeaderEntry.init(testing.allocator, "foo", "bar", null);85 var e = try HeaderEntry.init(testing.allocator, "foo", "bar", null);
88 defer e.deinit();86 defer e.deinit(testing.allocator);
89 testing.expectEqualSlices(u8, "foo", e.name);87 testing.expectEqualSlices(u8, "foo", e.name);
90 testing.expectEqualSlices(u8, "bar", e.value);88 testing.expectEqualSlices(u8, "bar", e.value);
91 testing.expectEqual(false, e.never_index);89 testing.expectEqual(false, e.never_index);
9290
93 try e.modify("longer value", null);91 try e.modify(testing.allocator, "longer value", null);
94 testing.expectEqualSlices(u8, "longer value", e.value);92 testing.expectEqualSlices(u8, "longer value", e.value);
9593
96 // shorter value94 // shorter value
97 try e.modify("x", null);95 try e.modify(testing.allocator, "x", null);
98 testing.expectEqualSlices(u8, "x", e.value);96 testing.expectEqualSlices(u8, "x", e.value);
99}97}
10098
101const HeaderList = std.ArrayList(HeaderEntry);99const HeaderList = std.ArrayListUnmanaged(HeaderEntry);
102const HeaderIndexList = std.ArrayList(usize);100const HeaderIndexList = std.ArrayListUnmanaged(usize);
103const HeaderIndex = std.StringHashMap(HeaderIndexList);101const HeaderIndex = std.StringHashMapUnmanaged(HeaderIndexList);
104102
105pub const Headers = struct {103pub const Headers = struct {
106 // the owned header field name is stored in the index as part of the key104 // the owned header field name is stored in the index as part of the key
...@@ -113,62 +111,62 @@ pub const Headers = struct {...@@ -113,62 +111,62 @@ pub const Headers = struct {
113 pub fn init(allocator: *Allocator) Self {111 pub fn init(allocator: *Allocator) Self {
114 return Self{112 return Self{
115 .allocator = allocator,113 .allocator = allocator,
116 .data = HeaderList.init(allocator),114 .data = HeaderList{},
117 .index = HeaderIndex.init(allocator),115 .index = HeaderIndex{},
118 };116 };
119 }117 }
120118
121 pub fn deinit(self: Self) void {119 pub fn deinit(self: *Self) void {
122 {120 {
123 var it = self.index.iterator();121 for (self.index.items()) |*entry| {
124 while (it.next()) |kv| {122 const dex = &entry.value;
125 var dex = &kv.value;123 dex.deinit(self.allocator);
126 dex.deinit();124 self.allocator.free(entry.key);
127 self.allocator.free(kv.key);
128 }125 }
129 self.index.deinit();126 self.index.deinit(self.allocator);
130 }127 }
131 {128 {
132 for (self.data.span()) |entry| {129 for (self.data.items) |entry| {
133 entry.deinit();130 entry.deinit(self.allocator);
134 }131 }
135 self.data.deinit();132 self.data.deinit(self.allocator);
136 }133 }
134 self.* = undefined;
137 }135 }
138136
139 pub fn clone(self: Self, allocator: *Allocator) !Self {137 pub fn clone(self: Self, allocator: *Allocator) !Self {
140 var other = Headers.init(allocator);138 var other = Headers.init(allocator);
141 errdefer other.deinit();139 errdefer other.deinit();
142 try other.data.ensureCapacity(self.data.items.len);140 try other.data.ensureCapacity(allocator, self.data.items.len);
143 try other.index.initCapacity(self.index.entries.len);141 try other.index.initCapacity(allocator, self.index.entries.len);
144 for (self.data.span()) |entry| {142 for (self.data.items) |entry| {
145 try other.append(entry.name, entry.value, entry.never_index);143 try other.append(entry.name, entry.value, entry.never_index);
146 }144 }
147 return other;145 return other;
148 }146 }
149147
150 pub fn toSlice(self: Self) []const HeaderEntry {148 pub fn toSlice(self: Self) []const HeaderEntry {
151 return self.data.span();149 return self.data.items;
152 }150 }
153151
154 pub fn append(self: *Self, name: []const u8, value: []const u8, never_index: ?bool) !void {152 pub fn append(self: *Self, name: []const u8, value: []const u8, never_index: ?bool) !void {
155 const n = self.data.items.len + 1;153 const n = self.data.items.len + 1;
156 try self.data.ensureCapacity(n);154 try self.data.ensureCapacity(self.allocator, n);
157 var entry: HeaderEntry = undefined;155 var entry: HeaderEntry = undefined;
158 if (self.index.get(name)) |kv| {156 if (self.index.getEntry(name)) |kv| {
159 entry = try HeaderEntry.init(self.allocator, kv.key, value, never_index);157 entry = try HeaderEntry.init(self.allocator, kv.key, value, never_index);
160 errdefer entry.deinit();158 errdefer entry.deinit(self.allocator);
161 var dex = &kv.value;159 const dex = &kv.value;
162 try dex.append(n - 1);160 try dex.append(self.allocator, n - 1);
163 } else {161 } else {
164 const name_dup = try mem.dupe(self.allocator, u8, name);162 const name_dup = try self.allocator.dupe(u8, name);
165 errdefer self.allocator.free(name_dup);163 errdefer self.allocator.free(name_dup);
166 entry = try HeaderEntry.init(self.allocator, name_dup, value, never_index);164 entry = try HeaderEntry.init(self.allocator, name_dup, value, never_index);
167 errdefer entry.deinit();165 errdefer entry.deinit(self.allocator);
168 var dex = HeaderIndexList.init(self.allocator);166 var dex = HeaderIndexList{};
169 try dex.append(n - 1);167 try dex.append(self.allocator, n - 1);
170 errdefer dex.deinit();168 errdefer dex.deinit(self.allocator);
171 _ = try self.index.put(name_dup, dex);169 _ = try self.index.put(self.allocator, name_dup, dex);
172 }170 }
173 self.data.appendAssumeCapacity(entry);171 self.data.appendAssumeCapacity(entry);
174 }172 }
...@@ -194,8 +192,8 @@ pub const Headers = struct {...@@ -194,8 +192,8 @@ pub const Headers = struct {
194192
195 /// Returns boolean indicating if something was deleted.193 /// Returns boolean indicating if something was deleted.
196 pub fn delete(self: *Self, name: []const u8) bool {194 pub fn delete(self: *Self, name: []const u8) bool {
197 if (self.index.remove(name)) |kv| {195 if (self.index.remove(name)) |*kv| {
198 var dex = &kv.value;196 const dex = &kv.value;
199 // iterate backwards197 // iterate backwards
200 var i = dex.items.len;198 var i = dex.items.len;
201 while (i > 0) {199 while (i > 0) {
...@@ -203,11 +201,11 @@ pub const Headers = struct {...@@ -203,11 +201,11 @@ pub const Headers = struct {
203 const data_index = dex.items[i];201 const data_index = dex.items[i];
204 const removed = self.data.orderedRemove(data_index);202 const removed = self.data.orderedRemove(data_index);
205 assert(mem.eql(u8, removed.name, name));203 assert(mem.eql(u8, removed.name, name));
206 removed.deinit();204 removed.deinit(self.allocator);
207 }205 }
208 dex.deinit();206 dex.deinit(self.allocator);
209 self.allocator.free(kv.key);207 self.allocator.free(kv.key);
210 self.rebuild_index();208 self.rebuildIndex();
211 return true;209 return true;
212 } else {210 } else {
213 return false;211 return false;
...@@ -216,45 +214,52 @@ pub const Headers = struct {...@@ -216,45 +214,52 @@ pub const Headers = struct {
216214
217 /// Removes the element at the specified index.215 /// Removes the element at the specified index.
218 /// Moves items down to fill the empty space.216 /// Moves items down to fill the empty space.
217 /// TODO this implementation can be replaced by adding
218 /// orderedRemove to the new hash table implementation as an
219 /// alternative to swapRemove.
219 pub fn orderedRemove(self: *Self, i: usize) void {220 pub fn orderedRemove(self: *Self, i: usize) void {
220 const removed = self.data.orderedRemove(i);221 const removed = self.data.orderedRemove(i);
221 const kv = self.index.get(removed.name).?;222 const kv = self.index.getEntry(removed.name).?;
222 var dex = &kv.value;223 const dex = &kv.value;
223 if (dex.items.len == 1) {224 if (dex.items.len == 1) {
224 // was last item; delete the index225 // was last item; delete the index
225 _ = self.index.remove(kv.key);226 dex.deinit(self.allocator);
226 dex.deinit();227 removed.deinit(self.allocator);
227 removed.deinit();228 const key = kv.key;
228 self.allocator.free(kv.key);229 _ = self.index.remove(key); // invalidates `kv` and `dex`
230 self.allocator.free(key);
229 } else {231 } else {
230 dex.shrink(dex.items.len - 1);232 dex.shrink(self.allocator, dex.items.len - 1);
231 removed.deinit();233 removed.deinit(self.allocator);
232 }234 }
233 // if it was the last item; no need to rebuild index235 // if it was the last item; no need to rebuild index
234 if (i != self.data.items.len) {236 if (i != self.data.items.len) {
235 self.rebuild_index();237 self.rebuildIndex();
236 }238 }
237 }239 }
238240
239 /// Removes the element at the specified index.241 /// Removes the element at the specified index.
240 /// The empty slot is filled from the end of the list.242 /// The empty slot is filled from the end of the list.
243 /// TODO this implementation can be replaced by simply using the
244 /// new hash table which does swap removal.
241 pub fn swapRemove(self: *Self, i: usize) void {245 pub fn swapRemove(self: *Self, i: usize) void {
242 const removed = self.data.swapRemove(i);246 const removed = self.data.swapRemove(i);
243 const kv = self.index.get(removed.name).?;247 const kv = self.index.getEntry(removed.name).?;
244 var dex = &kv.value;248 const dex = &kv.value;
245 if (dex.items.len == 1) {249 if (dex.items.len == 1) {
246 // was last item; delete the index250 // was last item; delete the index
247 _ = self.index.remove(kv.key);251 dex.deinit(self.allocator);
248 dex.deinit();252 removed.deinit(self.allocator);
249 removed.deinit();253 const key = kv.key;
250 self.allocator.free(kv.key);254 _ = self.index.remove(key); // invalidates `kv` and `dex`
255 self.allocator.free(key);
251 } else {256 } else {
252 dex.shrink(dex.items.len - 1);257 dex.shrink(self.allocator, dex.items.len - 1);
253 removed.deinit();258 removed.deinit(self.allocator);
254 }259 }
255 // if it was the last item; no need to rebuild index260 // if it was the last item; no need to rebuild index
256 if (i != self.data.items.len) {261 if (i != self.data.items.len) {
257 self.rebuild_index();262 self.rebuildIndex();
258 }263 }
259 }264 }
260265
...@@ -266,11 +271,7 @@ pub const Headers = struct {...@@ -266,11 +271,7 @@ pub const Headers = struct {
266 /// Returns a list of indices containing headers with the given name.271 /// Returns a list of indices containing headers with the given name.
267 /// The returned list should not be modified by the caller.272 /// The returned list should not be modified by the caller.
268 pub fn getIndices(self: Self, name: []const u8) ?HeaderIndexList {273 pub fn getIndices(self: Self, name: []const u8) ?HeaderIndexList {
269 if (self.index.get(name)) |kv| {274 return self.index.get(name);
270 return kv.value;
271 } else {
272 return null;
273 }
274 }275 }
275276
276 /// Returns a slice containing each header with the given name.277 /// Returns a slice containing each header with the given name.
...@@ -279,7 +280,7 @@ pub const Headers = struct {...@@ -279,7 +280,7 @@ pub const Headers = struct {
279280
280 const buf = try allocator.alloc(HeaderEntry, dex.items.len);281 const buf = try allocator.alloc(HeaderEntry, dex.items.len);
281 var n: usize = 0;282 var n: usize = 0;
282 for (dex.span()) |idx| {283 for (dex.items) |idx| {
283 buf[n] = self.data.items[idx];284 buf[n] = self.data.items[idx];
284 n += 1;285 n += 1;
285 }286 }
...@@ -302,7 +303,7 @@ pub const Headers = struct {...@@ -302,7 +303,7 @@ pub const Headers = struct {
302 // adapted from mem.join303 // adapted from mem.join
303 const total_len = blk: {304 const total_len = blk: {
304 var sum: usize = dex.items.len - 1; // space for separator(s)305 var sum: usize = dex.items.len - 1; // space for separator(s)
305 for (dex.span()) |idx|306 for (dex.items) |idx|
306 sum += self.data.items[idx].value.len;307 sum += self.data.items[idx].value.len;
307 break :blk sum;308 break :blk sum;
308 };309 };
...@@ -325,32 +326,27 @@ pub const Headers = struct {...@@ -325,32 +326,27 @@ pub const Headers = struct {
325 return buf;326 return buf;
326 }327 }
327328
328 fn rebuild_index(self: *Self) void {329 fn rebuildIndex(self: *Self) void {
329 { // clear out the indexes330 // clear out the indexes
330 var it = self.index.iterator();331 for (self.index.items()) |*entry| {
331 while (it.next()) |kv| {332 entry.value.shrinkRetainingCapacity(0);
332 var dex = &kv.value;
333 dex.items.len = 0; // keeps capacity available
334 }
335 }333 }
336 { // fill up indexes again; we know capacity is fine from before334 // fill up indexes again; we know capacity is fine from before
337 for (self.data.span()) |entry, i| {335 for (self.data.items) |entry, i| {
338 var dex = &self.index.get(entry.name).?.value;336 self.index.getEntry(entry.name).?.value.appendAssumeCapacity(i);
339 dex.appendAssumeCapacity(i);
340 }
341 }337 }
342 }338 }
343339
344 pub fn sort(self: *Self) void {340 pub fn sort(self: *Self) void {
345 std.sort.sort(HeaderEntry, self.data.items, {}, HeaderEntry.compare);341 std.sort.sort(HeaderEntry, self.data.items, {}, HeaderEntry.compare);
346 self.rebuild_index();342 self.rebuildIndex();
347 }343 }
348344
349 pub fn format(345 pub fn format(
350 self: Self,346 self: Self,
351 comptime fmt: []const u8,347 comptime fmt: []const u8,
352 options: std.fmt.FormatOptions,348 options: std.fmt.FormatOptions,
353 out_stream: var,349 out_stream: anytype,
354 ) !void {350 ) !void {
355 for (self.toSlice()) |entry| {351 for (self.toSlice()) |entry| {
356 try out_stream.writeAll(entry.name);352 try out_stream.writeAll(entry.name);
...@@ -495,8 +491,8 @@ test "Headers.getIndices" {...@@ -495,8 +491,8 @@ test "Headers.getIndices" {
495 try h.append("set-cookie", "y=2", null);491 try h.append("set-cookie", "y=2", null);
496492
497 testing.expect(null == h.getIndices("not-present"));493 testing.expect(null == h.getIndices("not-present"));
498 testing.expectEqualSlices(usize, &[_]usize{0}, h.getIndices("foo").?.span());494 testing.expectEqualSlices(usize, &[_]usize{0}, h.getIndices("foo").?.items);
499 testing.expectEqualSlices(usize, &[_]usize{ 1, 2 }, h.getIndices("set-cookie").?.span());495 testing.expectEqualSlices(usize, &[_]usize{ 1, 2 }, h.getIndices("set-cookie").?.items);
500}496}
501497
502test "Headers.get" {498test "Headers.get" {
lib/std/io/bit_reader.zig+1-1
...@@ -170,7 +170,7 @@ pub fn BitReader(endian: builtin.Endian, comptime ReaderType: type) type {...@@ -170,7 +170,7 @@ pub fn BitReader(endian: builtin.Endian, comptime ReaderType: type) type {
170170
171pub fn bitReader(171pub fn bitReader(
172 comptime endian: builtin.Endian,172 comptime endian: builtin.Endian,
173 underlying_stream: var,173 underlying_stream: anytype,
174) BitReader(endian, @TypeOf(underlying_stream)) {174) BitReader(endian, @TypeOf(underlying_stream)) {
175 return BitReader(endian, @TypeOf(underlying_stream)).init(underlying_stream);175 return BitReader(endian, @TypeOf(underlying_stream)).init(underlying_stream);
176}176}
lib/std/io/bit_writer.zig+2-2
...@@ -34,7 +34,7 @@ pub fn BitWriter(endian: builtin.Endian, comptime WriterType: type) type {...@@ -34,7 +34,7 @@ pub fn BitWriter(endian: builtin.Endian, comptime WriterType: type) type {
34 /// Write the specified number of bits to the stream from the least significant bits of34 /// Write the specified number of bits to the stream from the least significant bits of
35 /// the specified unsigned int value. Bits will only be written to the stream when there35 /// the specified unsigned int value. Bits will only be written to the stream when there
36 /// are enough to fill a byte.36 /// are enough to fill a byte.
37 pub fn writeBits(self: *Self, value: var, bits: usize) Error!void {37 pub fn writeBits(self: *Self, value: anytype, bits: usize) Error!void {
38 if (bits == 0) return;38 if (bits == 0) return;
3939
40 const U = @TypeOf(value);40 const U = @TypeOf(value);
...@@ -145,7 +145,7 @@ pub fn BitWriter(endian: builtin.Endian, comptime WriterType: type) type {...@@ -145,7 +145,7 @@ pub fn BitWriter(endian: builtin.Endian, comptime WriterType: type) type {
145145
146pub fn bitWriter(146pub fn bitWriter(
147 comptime endian: builtin.Endian,147 comptime endian: builtin.Endian,
148 underlying_stream: var,148 underlying_stream: anytype,
149) BitWriter(endian, @TypeOf(underlying_stream)) {149) BitWriter(endian, @TypeOf(underlying_stream)) {
150 return BitWriter(endian, @TypeOf(underlying_stream)).init(underlying_stream);150 return BitWriter(endian, @TypeOf(underlying_stream)).init(underlying_stream);
151}151}
lib/std/io/buffered_out_stream.zig+1-1
...@@ -2,4 +2,4 @@...@@ -2,4 +2,4 @@
2pub const BufferedOutStream = @import("./buffered_writer.zig").BufferedWriter;2pub const BufferedOutStream = @import("./buffered_writer.zig").BufferedWriter;
33
4/// Deprecated: use `std.io.buffered_writer.bufferedWriter`4/// Deprecated: use `std.io.buffered_writer.bufferedWriter`
5pub const bufferedOutStream = @import("./buffered_writer.zig").bufferedWriter5pub const bufferedOutStream = @import("./buffered_writer.zig").bufferedWriter;
lib/std/io/buffered_reader.zig+1-1
...@@ -48,7 +48,7 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty...@@ -48,7 +48,7 @@ pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) ty
48 };48 };
49}49}
5050
51pub fn bufferedReader(underlying_stream: var) BufferedReader(4096, @TypeOf(underlying_stream)) {51pub fn bufferedReader(underlying_stream: anytype) BufferedReader(4096, @TypeOf(underlying_stream)) {
52 return .{ .unbuffered_reader = underlying_stream };52 return .{ .unbuffered_reader = underlying_stream };
53}53}
5454
lib/std/io/buffered_writer.zig+1-1
...@@ -43,6 +43,6 @@ pub fn BufferedWriter(comptime buffer_size: usize, comptime WriterType: type) ty...@@ -43,6 +43,6 @@ pub fn BufferedWriter(comptime buffer_size: usize, comptime WriterType: type) ty
43 };43 };
44}44}
4545
46pub fn bufferedWriter(underlying_stream: var) BufferedWriter(4096, @TypeOf(underlying_stream)) {46pub fn bufferedWriter(underlying_stream: anytype) BufferedWriter(4096, @TypeOf(underlying_stream)) {
47 return .{ .unbuffered_writer = underlying_stream };47 return .{ .unbuffered_writer = underlying_stream };
48}48}
lib/std/io/counting_writer.zig+1-1
...@@ -32,7 +32,7 @@ pub fn CountingWriter(comptime WriterType: type) type {...@@ -32,7 +32,7 @@ pub fn CountingWriter(comptime WriterType: type) type {
32 };32 };
33}33}
3434
35pub fn countingWriter(child_stream: var) CountingWriter(@TypeOf(child_stream)) {35pub fn countingWriter(child_stream: anytype) CountingWriter(@TypeOf(child_stream)) {
36 return .{ .bytes_written = 0, .child_stream = child_stream };36 return .{ .bytes_written = 0, .child_stream = child_stream };
37}37}
3838
lib/std/io/fixed_buffer_stream.zig+1-1
...@@ -127,7 +127,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type {...@@ -127,7 +127,7 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
127 };127 };
128}128}
129129
130pub fn fixedBufferStream(buffer: var) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) {130pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) {
131 return .{ .buffer = mem.span(buffer), .pos = 0 };131 return .{ .buffer = mem.span(buffer), .pos = 0 };
132}132}
133133
lib/std/io/multi_writer.zig+1-1
...@@ -43,7 +43,7 @@ pub fn MultiWriter(comptime Writers: type) type {...@@ -43,7 +43,7 @@ pub fn MultiWriter(comptime Writers: type) type {
43 };43 };
44}44}
4545
46pub fn multiWriter(streams: var) MultiWriter(@TypeOf(streams)) {46pub fn multiWriter(streams: anytype) MultiWriter(@TypeOf(streams)) {
47 return .{ .streams = streams };47 return .{ .streams = streams };
48}48}
4949
lib/std/io/peek_stream.zig+1-1
...@@ -80,7 +80,7 @@ pub fn PeekStream(...@@ -80,7 +80,7 @@ pub fn PeekStream(
8080
81pub fn peekStream(81pub fn peekStream(
82 comptime lookahead: comptime_int,82 comptime lookahead: comptime_int,
83 underlying_stream: var,83 underlying_stream: anytype,
84) PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)) {84) PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)) {
85 return PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)).init(underlying_stream);85 return PeekStream(.{ .Static = lookahead }, @TypeOf(underlying_stream)).init(underlying_stream);
86}86}
lib/std/io/reader.zig+1-2
...@@ -40,8 +40,7 @@ pub fn Reader(...@@ -40,8 +40,7 @@ pub fn Reader(
40 return index;40 return index;
41 }41 }
4242
43 /// Returns the number of bytes read. If the number read would be smaller than buf.len,43 /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.
44 /// error.EndOfStream is returned instead.
45 pub fn readNoEof(self: Self, buf: []u8) !void {44 pub fn readNoEof(self: Self, buf: []u8) !void {
46 const amt_read = try self.readAll(buf);45 const amt_read = try self.readAll(buf);
47 if (amt_read < buf.len) return error.EndOfStream;46 if (amt_read < buf.len) return error.EndOfStream;
lib/std/io/serialization.zig+33-29
...@@ -16,14 +16,16 @@ pub const Packing = enum {...@@ -16,14 +16,16 @@ pub const Packing = enum {
16};16};
1717
18/// Creates a deserializer that deserializes types from any stream.18/// Creates a deserializer that deserializes types from any stream.
19/// If `is_packed` is true, the data stream is treated as bit-packed,19/// If `is_packed` is true, the data stream is treated as bit-packed,
20/// otherwise data is expected to be packed to the smallest byte.20/// otherwise data is expected to be packed to the smallest byte.
21/// Types may implement a custom deserialization routine with a21/// Types may implement a custom deserialization routine with a
22/// function named `deserialize` in the form of:22/// function named `deserialize` in the form of:
23/// pub fn deserialize(self: *Self, deserializer: var) !void23/// ```
24/// which will be called when the deserializer is used to deserialize24/// pub fn deserialize(self: *Self, deserializer: anytype) !void
25/// that type. It will pass a pointer to the type instance to deserialize25/// ```
26/// into and a pointer to the deserializer struct.26/// which will be called when the deserializer is used to deserialize
27/// that type. It will pass a pointer to the type instance to deserialize
28/// into and a pointer to the deserializer struct.
27pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime ReaderType: type) type {29pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime ReaderType: type) type {
28 return struct {30 return struct {
29 in_stream: if (packing == .Bit) io.BitReader(endian, ReaderType) else ReaderType,31 in_stream: if (packing == .Bit) io.BitReader(endian, ReaderType) else ReaderType,
...@@ -93,7 +95,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,...@@ -93,7 +95,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
93 }95 }
9496
95 /// Deserializes data into the type pointed to by `ptr`97 /// Deserializes data into the type pointed to by `ptr`
96 pub fn deserializeInto(self: *Self, ptr: var) !void {98 pub fn deserializeInto(self: *Self, ptr: anytype) !void {
97 const T = @TypeOf(ptr);99 const T = @TypeOf(ptr);
98 comptime assert(trait.is(.Pointer)(T));100 comptime assert(trait.is(.Pointer)(T));
99101
...@@ -108,7 +110,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,...@@ -108,7 +110,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
108 const C = comptime meta.Child(T);110 const C = comptime meta.Child(T);
109 const child_type_id = @typeInfo(C);111 const child_type_id = @typeInfo(C);
110112
111 //custom deserializer: fn(self: *Self, deserializer: var) !void113 //custom deserializer: fn(self: *Self, deserializer: anytype) !void
112 if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);114 if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
113115
114 if (comptime trait.isPacked(C) and packing != .Bit) {116 if (comptime trait.isPacked(C) and packing != .Bit) {
...@@ -190,24 +192,26 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,...@@ -190,24 +192,26 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
190pub fn deserializer(192pub fn deserializer(
191 comptime endian: builtin.Endian,193 comptime endian: builtin.Endian,
192 comptime packing: Packing,194 comptime packing: Packing,
193 in_stream: var,195 in_stream: anytype,
194) Deserializer(endian, packing, @TypeOf(in_stream)) {196) Deserializer(endian, packing, @TypeOf(in_stream)) {
195 return Deserializer(endian, packing, @TypeOf(in_stream)).init(in_stream);197 return Deserializer(endian, packing, @TypeOf(in_stream)).init(in_stream);
196}198}
197199
198/// Creates a serializer that serializes types to any stream.200/// Creates a serializer that serializes types to any stream.
199/// If `is_packed` is true, the data will be bit-packed into the stream.201/// If `is_packed` is true, the data will be bit-packed into the stream.
200/// Note that the you must call `serializer.flush()` when you are done202/// Note that the you must call `serializer.flush()` when you are done
201/// writing bit-packed data in order ensure any unwritten bits are committed.203/// writing bit-packed data in order ensure any unwritten bits are committed.
202/// If `is_packed` is false, data is packed to the smallest byte. In the case204/// If `is_packed` is false, data is packed to the smallest byte. In the case
203/// of packed structs, the struct will written bit-packed and with the specified205/// of packed structs, the struct will written bit-packed and with the specified
204/// endianess, after which data will resume being written at the next byte boundary.206/// endianess, after which data will resume being written at the next byte boundary.
205/// Types may implement a custom serialization routine with a207/// Types may implement a custom serialization routine with a
206/// function named `serialize` in the form of:208/// function named `serialize` in the form of:
207/// pub fn serialize(self: Self, serializer: var) !void209/// ```
208/// which will be called when the serializer is used to serialize that type. It will210/// pub fn serialize(self: Self, serializer: anytype) !void
209/// pass a const pointer to the type instance to be serialized and a pointer211/// ```
210/// to the serializer struct.212/// which will be called when the serializer is used to serialize that type. It will
213/// pass a const pointer to the type instance to be serialized and a pointer
214/// to the serializer struct.
211pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime OutStreamType: type) type {215pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime OutStreamType: type) type {
212 return struct {216 return struct {
213 out_stream: if (packing == .Bit) io.BitOutStream(endian, OutStreamType) else OutStreamType,217 out_stream: if (packing == .Bit) io.BitOutStream(endian, OutStreamType) else OutStreamType,
...@@ -229,7 +233,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co...@@ -229,7 +233,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
229 if (packing == .Bit) return self.out_stream.flushBits();233 if (packing == .Bit) return self.out_stream.flushBits();
230 }234 }
231235
232 fn serializeInt(self: *Self, value: var) Error!void {236 fn serializeInt(self: *Self, value: anytype) Error!void {
233 const T = @TypeOf(value);237 const T = @TypeOf(value);
234 comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));238 comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
235239
...@@ -261,7 +265,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co...@@ -261,7 +265,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
261 }265 }
262266
263 /// Serializes the passed value into the stream267 /// Serializes the passed value into the stream
264 pub fn serialize(self: *Self, value: var) Error!void {268 pub fn serialize(self: *Self, value: anytype) Error!void {
265 const T = comptime @TypeOf(value);269 const T = comptime @TypeOf(value);
266270
267 if (comptime trait.isIndexable(T)) {271 if (comptime trait.isIndexable(T)) {
...@@ -270,7 +274,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co...@@ -270,7 +274,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
270 return;274 return;
271 }275 }
272276
273 //custom serializer: fn(self: Self, serializer: var) !void277 //custom serializer: fn(self: Self, serializer: anytype) !void
274 if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);278 if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
275279
276 if (comptime trait.isPacked(T) and packing != .Bit) {280 if (comptime trait.isPacked(T) and packing != .Bit) {
...@@ -346,7 +350,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co...@@ -346,7 +350,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
346pub fn serializer(350pub fn serializer(
347 comptime endian: builtin.Endian,351 comptime endian: builtin.Endian,
348 comptime packing: Packing,352 comptime packing: Packing,
349 out_stream: var,353 out_stream: anytype,
350) Serializer(endian, packing, @TypeOf(out_stream)) {354) Serializer(endian, packing, @TypeOf(out_stream)) {
351 return Serializer(endian, packing, @TypeOf(out_stream)).init(out_stream);355 return Serializer(endian, packing, @TypeOf(out_stream)).init(out_stream);
352}356}
...@@ -462,7 +466,7 @@ test "Serializer/Deserializer Int: Inf/NaN" {...@@ -462,7 +466,7 @@ test "Serializer/Deserializer Int: Inf/NaN" {
462 try testIntSerializerDeserializerInfNaN(.Little, .Bit);466 try testIntSerializerDeserializerInfNaN(.Little, .Bit);
463}467}
464468
465fn testAlternateSerializer(self: var, _serializer: var) !void {469fn testAlternateSerializer(self: anytype, _serializer: anytype) !void {
466 try _serializer.serialize(self.f_f16);470 try _serializer.serialize(self.f_f16);
467}471}
468472
...@@ -503,7 +507,7 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing:...@@ -503,7 +507,7 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing:
503 f_f16: f16,507 f_f16: f16,
504 f_unused_u32: u32,508 f_unused_u32: u32,
505509
506 pub fn deserialize(self: *@This(), _deserializer: var) !void {510 pub fn deserialize(self: *@This(), _deserializer: anytype) !void {
507 try _deserializer.deserializeInto(&self.f_f16);511 try _deserializer.deserializeInto(&self.f_f16);
508 self.f_unused_u32 = 47;512 self.f_unused_u32 = 47;
509 }513 }
lib/std/io/writer.zig+1-1
...@@ -24,7 +24,7 @@ pub fn Writer(...@@ -24,7 +24,7 @@ pub fn Writer(
24 }24 }
25 }25 }
2626
27 pub fn print(self: Self, comptime format: []const u8, args: var) Error!void {27 pub fn print(self: Self, comptime format: []const u8, args: anytype) Error!void {
28 return std.fmt.format(self, format, args);28 return std.fmt.format(self, format, args);
29 }29 }
3030
lib/std/json.zig+43-44
...@@ -239,7 +239,7 @@ pub const StreamingParser = struct {...@@ -239,7 +239,7 @@ pub const StreamingParser = struct {
239 NullLiteral3,239 NullLiteral3,
240240
241 // Only call this function to generate array/object final state.241 // Only call this function to generate array/object final state.
242 pub fn fromInt(x: var) State {242 pub fn fromInt(x: anytype) State {
243 debug.assert(x == 0 or x == 1);243 debug.assert(x == 0 or x == 1);
244 const T = @TagType(State);244 const T = @TagType(State);
245 return @intToEnum(State, @intCast(T, x));245 return @intToEnum(State, @intCast(T, x));
...@@ -1236,7 +1236,7 @@ pub const Value = union(enum) {...@@ -1236,7 +1236,7 @@ pub const Value = union(enum) {
1236 pub fn jsonStringify(1236 pub fn jsonStringify(
1237 value: @This(),1237 value: @This(),
1238 options: StringifyOptions,1238 options: StringifyOptions,
1239 out_stream: var,1239 out_stream: anytype,
1240 ) @TypeOf(out_stream).Error!void {1240 ) @TypeOf(out_stream).Error!void {
1241 switch (value) {1241 switch (value) {
1242 .Null => try stringify(null, options, out_stream),1242 .Null => try stringify(null, options, out_stream),
...@@ -1288,7 +1288,7 @@ pub const Value = union(enum) {...@@ -1288,7 +1288,7 @@ pub const Value = union(enum) {
1288 var held = std.debug.getStderrMutex().acquire();1288 var held = std.debug.getStderrMutex().acquire();
1289 defer held.release();1289 defer held.release();
12901290
1291 const stderr = std.debug.getStderrStream();1291 const stderr = io.getStdErr().writer();
1292 std.json.stringify(self, std.json.StringifyOptions{ .whitespace = null }, stderr) catch return;1292 std.json.stringify(self, std.json.StringifyOptions{ .whitespace = null }, stderr) catch return;
1293 }1293 }
1294};1294};
...@@ -1535,7 +1535,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:...@@ -1535,7 +1535,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
1535 const allocator = options.allocator orelse return error.AllocatorRequired;1535 const allocator = options.allocator orelse return error.AllocatorRequired;
1536 switch (ptrInfo.size) {1536 switch (ptrInfo.size) {
1537 .One => {1537 .One => {
1538 const r: T = allocator.create(ptrInfo.child);1538 const r: T = try allocator.create(ptrInfo.child);
1539 r.* = try parseInternal(ptrInfo.child, token, tokens, options);1539 r.* = try parseInternal(ptrInfo.child, token, tokens, options);
1540 return r;1540 return r;
1541 },1541 },
...@@ -1567,7 +1567,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:...@@ -1567,7 +1567,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
1567 if (ptrInfo.child != u8) return error.UnexpectedToken;1567 if (ptrInfo.child != u8) return error.UnexpectedToken;
1568 const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);1568 const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);
1569 switch (stringToken.escapes) {1569 switch (stringToken.escapes) {
1570 .None => return mem.dupe(allocator, u8, source_slice),1570 .None => return allocator.dupe(u8, source_slice),
1571 .Some => |some_escapes| {1571 .Some => |some_escapes| {
1572 const output = try allocator.alloc(u8, stringToken.decodedLength());1572 const output = try allocator.alloc(u8, stringToken.decodedLength());
1573 errdefer allocator.free(output);1573 errdefer allocator.free(output);
...@@ -1629,7 +1629,7 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {...@@ -1629,7 +1629,7 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
1629 switch (ptrInfo.size) {1629 switch (ptrInfo.size) {
1630 .One => {1630 .One => {
1631 parseFree(ptrInfo.child, value.*, options);1631 parseFree(ptrInfo.child, value.*, options);
1632 allocator.destroy(v);1632 allocator.destroy(value);
1633 },1633 },
1634 .Slice => {1634 .Slice => {
1635 for (value) |v| {1635 for (value) |v| {
...@@ -2043,7 +2043,7 @@ pub const Parser = struct {...@@ -2043,7 +2043,7 @@ pub const Parser = struct {
2043 fn parseString(p: *Parser, allocator: *Allocator, s: std.meta.TagPayloadType(Token, Token.String), input: []const u8, i: usize) !Value {2043 fn parseString(p: *Parser, allocator: *Allocator, s: std.meta.TagPayloadType(Token, Token.String), input: []const u8, i: usize) !Value {
2044 const slice = s.slice(input, i);2044 const slice = s.slice(input, i);
2045 switch (s.escapes) {2045 switch (s.escapes) {
2046 .None => return Value{ .String = if (p.copy_strings) try mem.dupe(allocator, u8, slice) else slice },2046 .None => return Value{ .String = if (p.copy_strings) try allocator.dupe(u8, slice) else slice },
2047 .Some => |some_escapes| {2047 .Some => |some_escapes| {
2048 const output = try allocator.alloc(u8, s.decodedLength());2048 const output = try allocator.alloc(u8, s.decodedLength());
2049 errdefer allocator.free(output);2049 errdefer allocator.free(output);
...@@ -2149,27 +2149,27 @@ test "json.parser.dynamic" {...@@ -2149,27 +2149,27 @@ test "json.parser.dynamic" {
21492149
2150 var root = tree.root;2150 var root = tree.root;
21512151
2152 var image = root.Object.get("Image").?.value;2152 var image = root.Object.get("Image").?;
21532153
2154 const width = image.Object.get("Width").?.value;2154 const width = image.Object.get("Width").?;
2155 testing.expect(width.Integer == 800);2155 testing.expect(width.Integer == 800);
21562156
2157 const height = image.Object.get("Height").?.value;2157 const height = image.Object.get("Height").?;
2158 testing.expect(height.Integer == 600);2158 testing.expect(height.Integer == 600);
21592159
2160 const title = image.Object.get("Title").?.value;2160 const title = image.Object.get("Title").?;
2161 testing.expect(mem.eql(u8, title.String, "View from 15th Floor"));2161 testing.expect(mem.eql(u8, title.String, "View from 15th Floor"));
21622162
2163 const animated = image.Object.get("Animated").?.value;2163 const animated = image.Object.get("Animated").?;
2164 testing.expect(animated.Bool == false);2164 testing.expect(animated.Bool == false);
21652165
2166 const array_of_object = image.Object.get("ArrayOfObject").?.value;2166 const array_of_object = image.Object.get("ArrayOfObject").?;
2167 testing.expect(array_of_object.Array.items.len == 1);2167 testing.expect(array_of_object.Array.items.len == 1);
21682168
2169 const obj0 = array_of_object.Array.items[0].Object.get("n").?.value;2169 const obj0 = array_of_object.Array.items[0].Object.get("n").?;
2170 testing.expect(mem.eql(u8, obj0.String, "m"));2170 testing.expect(mem.eql(u8, obj0.String, "m"));
21712171
2172 const double = image.Object.get("double").?.value;2172 const double = image.Object.get("double").?;
2173 testing.expect(double.Float == 1.3412);2173 testing.expect(double.Float == 1.3412);
2174}2174}
21752175
...@@ -2217,12 +2217,12 @@ test "write json then parse it" {...@@ -2217,12 +2217,12 @@ test "write json then parse it" {
2217 var tree = try parser.parse(fixed_buffer_stream.getWritten());2217 var tree = try parser.parse(fixed_buffer_stream.getWritten());
2218 defer tree.deinit();2218 defer tree.deinit();
22192219
2220 testing.expect(tree.root.Object.get("f").?.value.Bool == false);2220 testing.expect(tree.root.Object.get("f").?.Bool == false);
2221 testing.expect(tree.root.Object.get("t").?.value.Bool == true);2221 testing.expect(tree.root.Object.get("t").?.Bool == true);
2222 testing.expect(tree.root.Object.get("int").?.value.Integer == 1234);2222 testing.expect(tree.root.Object.get("int").?.Integer == 1234);
2223 testing.expect(tree.root.Object.get("array").?.value.Array.items[0].Null == {});2223 testing.expect(tree.root.Object.get("array").?.Array.items[0].Null == {});
2224 testing.expect(tree.root.Object.get("array").?.value.Array.items[1].Float == 12.34);2224 testing.expect(tree.root.Object.get("array").?.Array.items[1].Float == 12.34);
2225 testing.expect(mem.eql(u8, tree.root.Object.get("str").?.value.String, "hello"));2225 testing.expect(mem.eql(u8, tree.root.Object.get("str").?.String, "hello"));
2226}2226}
22272227
2228fn test_parse(arena_allocator: *std.mem.Allocator, json_str: []const u8) !Value {2228fn test_parse(arena_allocator: *std.mem.Allocator, json_str: []const u8) !Value {
...@@ -2245,7 +2245,7 @@ test "integer after float has proper type" {...@@ -2245,7 +2245,7 @@ test "integer after float has proper type" {
2245 \\ "ints": [1, 2, 3]2245 \\ "ints": [1, 2, 3]
2246 \\}2246 \\}
2247 );2247 );
2248 std.testing.expect(json.Object.getValue("ints").?.Array.items[0] == .Integer);2248 std.testing.expect(json.Object.get("ints").?.Array.items[0] == .Integer);
2249}2249}
22502250
2251test "escaped characters" {2251test "escaped characters" {
...@@ -2271,16 +2271,16 @@ test "escaped characters" {...@@ -2271,16 +2271,16 @@ test "escaped characters" {
22712271
2272 const obj = (try test_parse(&arena_allocator.allocator, input)).Object;2272 const obj = (try test_parse(&arena_allocator.allocator, input)).Object;
22732273
2274 testing.expectEqualSlices(u8, obj.get("backslash").?.value.String, "\\");2274 testing.expectEqualSlices(u8, obj.get("backslash").?.String, "\\");
2275 testing.expectEqualSlices(u8, obj.get("forwardslash").?.value.String, "/");2275 testing.expectEqualSlices(u8, obj.get("forwardslash").?.String, "/");
2276 testing.expectEqualSlices(u8, obj.get("newline").?.value.String, "\n");2276 testing.expectEqualSlices(u8, obj.get("newline").?.String, "\n");
2277 testing.expectEqualSlices(u8, obj.get("carriagereturn").?.value.String, "\r");2277 testing.expectEqualSlices(u8, obj.get("carriagereturn").?.String, "\r");
2278 testing.expectEqualSlices(u8, obj.get("tab").?.value.String, "\t");2278 testing.expectEqualSlices(u8, obj.get("tab").?.String, "\t");
2279 testing.expectEqualSlices(u8, obj.get("formfeed").?.value.String, "\x0C");2279 testing.expectEqualSlices(u8, obj.get("formfeed").?.String, "\x0C");
2280 testing.expectEqualSlices(u8, obj.get("backspace").?.value.String, "\x08");2280 testing.expectEqualSlices(u8, obj.get("backspace").?.String, "\x08");
2281 testing.expectEqualSlices(u8, obj.get("doublequote").?.value.String, "\"");2281 testing.expectEqualSlices(u8, obj.get("doublequote").?.String, "\"");
2282 testing.expectEqualSlices(u8, obj.get("unicode").?.value.String, "ą");2282 testing.expectEqualSlices(u8, obj.get("unicode").?.String, "ą");
2283 testing.expectEqualSlices(u8, obj.get("surrogatepair").?.value.String, "😂");2283 testing.expectEqualSlices(u8, obj.get("surrogatepair").?.String, "😂");
2284}2284}
22852285
2286test "string copy option" {2286test "string copy option" {
...@@ -2306,11 +2306,11 @@ test "string copy option" {...@@ -2306,11 +2306,11 @@ test "string copy option" {
2306 const obj_copy = tree_copy.root.Object;2306 const obj_copy = tree_copy.root.Object;
23072307
2308 for ([_][]const u8{ "noescape", "simple", "unicode", "surrogatepair" }) |field_name| {2308 for ([_][]const u8{ "noescape", "simple", "unicode", "surrogatepair" }) |field_name| {
2309 testing.expectEqualSlices(u8, obj_nocopy.getValue(field_name).?.String, obj_copy.getValue(field_name).?.String);2309 testing.expectEqualSlices(u8, obj_nocopy.get(field_name).?.String, obj_copy.get(field_name).?.String);
2310 }2310 }
23112311
2312 const nocopy_addr = &obj_nocopy.getValue("noescape").?.String[0];2312 const nocopy_addr = &obj_nocopy.get("noescape").?.String[0];
2313 const copy_addr = &obj_copy.getValue("noescape").?.String[0];2313 const copy_addr = &obj_copy.get("noescape").?.String[0];
23142314
2315 var found_nocopy = false;2315 var found_nocopy = false;
2316 for (input) |_, index| {2316 for (input) |_, index| {
...@@ -2338,7 +2338,7 @@ pub const StringifyOptions = struct {...@@ -2338,7 +2338,7 @@ pub const StringifyOptions = struct {
23382338
2339 pub fn outputIndent(2339 pub fn outputIndent(
2340 whitespace: @This(),2340 whitespace: @This(),
2341 out_stream: var,2341 out_stream: anytype,
2342 ) @TypeOf(out_stream).Error!void {2342 ) @TypeOf(out_stream).Error!void {
2343 var char: u8 = undefined;2343 var char: u8 = undefined;
2344 var n_chars: usize = undefined;2344 var n_chars: usize = undefined;
...@@ -2380,7 +2380,7 @@ pub const StringifyOptions = struct {...@@ -2380,7 +2380,7 @@ pub const StringifyOptions = struct {
23802380
2381fn outputUnicodeEscape(2381fn outputUnicodeEscape(
2382 codepoint: u21,2382 codepoint: u21,
2383 out_stream: var,2383 out_stream: anytype,
2384) !void {2384) !void {
2385 if (codepoint <= 0xFFFF) {2385 if (codepoint <= 0xFFFF) {
2386 // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF),2386 // If the character is in the Basic Multilingual Plane (U+0000 through U+FFFF),
...@@ -2402,9 +2402,9 @@ fn outputUnicodeEscape(...@@ -2402,9 +2402,9 @@ fn outputUnicodeEscape(
2402}2402}
24032403
2404pub fn stringify(2404pub fn stringify(
2405 value: var,2405 value: anytype,
2406 options: StringifyOptions,2406 options: StringifyOptions,
2407 out_stream: var,2407 out_stream: anytype,
2408) @TypeOf(out_stream).Error!void {2408) @TypeOf(out_stream).Error!void {
2409 const T = @TypeOf(value);2409 const T = @TypeOf(value);
2410 switch (@typeInfo(T)) {2410 switch (@typeInfo(T)) {
...@@ -2576,15 +2576,15 @@ pub fn stringify(...@@ -2576,15 +2576,15 @@ pub fn stringify(
2576 },2576 },
2577 .Array => return stringify(&value, options, out_stream),2577 .Array => return stringify(&value, options, out_stream),
2578 .Vector => |info| {2578 .Vector => |info| {
2579 const array: [info.len]info.child = value;2579 const array: [info.len]info.child = value;
2580 return stringify(&array, options, out_stream);2580 return stringify(&array, options, out_stream);
2581 },2581 },
2582 else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),2582 else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
2583 }2583 }
2584 unreachable;2584 unreachable;
2585}2585}
25862586
2587fn teststringify(expected: []const u8, value: var, options: StringifyOptions) !void {2587fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions) !void {
2588 const ValidationOutStream = struct {2588 const ValidationOutStream = struct {
2589 const Self = @This();2589 const Self = @This();
2590 pub const OutStream = std.io.OutStream(*Self, Error, write);2590 pub const OutStream = std.io.OutStream(*Self, Error, write);
...@@ -2758,7 +2758,7 @@ test "stringify struct with custom stringifier" {...@@ -2758,7 +2758,7 @@ test "stringify struct with custom stringifier" {
2758 pub fn jsonStringify(2758 pub fn jsonStringify(
2759 value: Self,2759 value: Self,
2760 options: StringifyOptions,2760 options: StringifyOptions,
2761 out_stream: var,2761 out_stream: anytype,
2762 ) !void {2762 ) !void {
2763 try out_stream.writeAll("[\"something special\",");2763 try out_stream.writeAll("[\"something special\",");
2764 try stringify(42, options, out_stream);2764 try stringify(42, options, out_stream);
...@@ -2770,4 +2770,3 @@ test "stringify struct with custom stringifier" {...@@ -2770,4 +2770,3 @@ test "stringify struct with custom stringifier" {
2770test "stringify vector" {2770test "stringify vector" {
2771 try teststringify("[1,1]", @splat(2, @as(u32, 1)), StringifyOptions{});2771 try teststringify("[1,1]", @splat(2, @as(u32, 1)), StringifyOptions{});
2772}2772}
2773
lib/std/json/write_stream.zig+3-3
...@@ -152,7 +152,7 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {...@@ -152,7 +152,7 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
152 self: *Self,152 self: *Self,
153 /// An integer, float, or `std.math.BigInt`. Emitted as a bare number if it fits losslessly153 /// An integer, float, or `std.math.BigInt`. Emitted as a bare number if it fits losslessly
154 /// in a IEEE 754 double float, otherwise emitted as a string to the full precision.154 /// in a IEEE 754 double float, otherwise emitted as a string to the full precision.
155 value: var,155 value: anytype,
156 ) !void {156 ) !void {
157 assert(self.state[self.state_index] == State.Value);157 assert(self.state[self.state_index] == State.Value);
158 switch (@typeInfo(@TypeOf(value))) {158 switch (@typeInfo(@TypeOf(value))) {
...@@ -215,7 +215,7 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {...@@ -215,7 +215,7 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
215 self.state_index -= 1;215 self.state_index -= 1;
216 }216 }
217217
218 fn stringify(self: *Self, value: var) !void {218 fn stringify(self: *Self, value: anytype) !void {
219 try std.json.stringify(value, std.json.StringifyOptions{219 try std.json.stringify(value, std.json.StringifyOptions{
220 .whitespace = self.whitespace,220 .whitespace = self.whitespace,
221 }, self.stream);221 }, self.stream);
...@@ -224,7 +224,7 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {...@@ -224,7 +224,7 @@ pub fn WriteStream(comptime OutStream: type, comptime max_depth: usize) type {
224}224}
225225
226pub fn writeStream(226pub fn writeStream(
227 out_stream: var,227 out_stream: anytype,
228 comptime max_depth: usize,228 comptime max_depth: usize,
229) WriteStream(@TypeOf(out_stream), max_depth) {229) WriteStream(@TypeOf(out_stream), max_depth) {
230 return WriteStream(@TypeOf(out_stream), max_depth).init(out_stream);230 return WriteStream(@TypeOf(out_stream), max_depth).init(out_stream);
lib/std/log.zig created+202
...@@ -0,0 +1,202 @@
1const std = @import("std.zig");
2const builtin = std.builtin;
3const root = @import("root");
4
5//! std.log is standardized interface for logging which allows for the logging
6//! of programs and libraries using this interface to be formatted and filtered
7//! by the implementer of the root.log function.
8//!
9//! The scope parameter should be used to give context to the logging. For
10//! example, a library called 'libfoo' might use .libfoo as its scope.
11//!
12//! An example root.log might look something like this:
13//!
14//! ```
15//! const std = @import("std");
16//!
17//! // Set the log level to warning
18//! pub const log_level: std.log.Level = .warn;
19//!
20//! // Define root.log to override the std implementation
21//! pub fn log(
22//! comptime level: std.log.Level,
23//! comptime scope: @TypeOf(.EnumLiteral),
24//! comptime format: []const u8,
25//! args: anytype,
26//! ) void {
27//! // Ignore all non-critical logging from sources other than
28//! // .my_project and .nice_library
29//! const scope_prefix = "(" ++ switch (scope) {
30//! .my_project, .nice_library => @tagName(scope),
31//! else => if (@enumToInt(level) <= @enumToInt(std.log.Level.crit))
32//! @tagName(scope)
33//! else
34//! return,
35//! } ++ "): ";
36//!
37//! const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
38//!
39//! // Print the message to stderr, silently ignoring any errors
40//! const held = std.debug.getStderrMutex().acquire();
41//! defer held.release();
42//! const stderr = std.debug.getStderrStream();
43//! nosuspend stderr.print(prefix ++ format, args) catch return;
44//! }
45//!
46//! pub fn main() void {
47//! // Won't be printed as log_level is .warn
48//! std.log.info(.my_project, "Starting up.\n", .{});
49//! std.log.err(.nice_library, "Something went very wrong, sorry.\n", .{});
50//! // Won't be printed as it gets filtered out by our log function
51//! std.log.err(.lib_that_logs_too_much, "Added 1 + 1\n", .{});
52//! }
53//! ```
54//! Which produces the following output:
55//! ```
56//! [err] (nice_library): Something went very wrong, sorry.
57//! ```
58
59pub const Level = enum {
60 /// Emergency: a condition that cannot be handled, usually followed by a
61 /// panic.
62 emerg,
63 /// Alert: a condition that should be corrected immediately (e.g. database
64 /// corruption).
65 alert,
66 /// Critical: A bug has been detected or something has gone wrong and it
67 /// will have an effect on the operation of the program.
68 crit,
69 /// Error: A bug has been detected or something has gone wrong but it is
70 /// recoverable.
71 err,
72 /// Warning: it is uncertain if something has gone wrong or not, but the
73 /// circumstances would be worth investigating.
74 warn,
75 /// Notice: non-error but significant conditions.
76 notice,
77 /// Informational: general messages about the state of the program.
78 info,
79 /// Debug: messages only useful for debugging.
80 debug,
81};
82
83/// The default log level is based on build mode. Note that in ReleaseSmall
84/// builds the default level is emerg but no messages will be stored/logged
85/// by the default logger to save space.
86pub const default_level: Level = switch (builtin.mode) {
87 .Debug => .debug,
88 .ReleaseSafe => .notice,
89 .ReleaseFast => .err,
90 .ReleaseSmall => .emerg,
91};
92
93/// The current log level. This is set to root.log_level if present, otherwise
94/// log.default_level.
95pub const level: Level = if (@hasDecl(root, "log_level"))
96 root.log_level
97else
98 default_level;
99
100fn log(
101 comptime message_level: Level,
102 comptime scope: @Type(.EnumLiteral),
103 comptime format: []const u8,
104 args: anytype,
105) void {
106 if (@enumToInt(message_level) <= @enumToInt(level)) {
107 if (@hasDecl(root, "log")) {
108 root.log(message_level, scope, format, args);
109 } else if (builtin.mode != .ReleaseSmall) {
110 const held = std.debug.getStderrMutex().acquire();
111 defer held.release();
112 const stderr = std.io.getStdErr().writer();
113 nosuspend stderr.print(format, args) catch return;
114 }
115 }
116}
117
118/// Log an emergency message to stderr. This log level is intended to be used
119/// for conditions that cannot be handled and is usually followed by a panic.
120pub fn emerg(
121 comptime scope: @Type(.EnumLiteral),
122 comptime format: []const u8,
123 args: anytype,
124) void {
125 @setCold(true);
126 log(.emerg, scope, format, args);
127}
128
129/// Log an alert message to stderr. This log level is intended to be used for
130/// conditions that should be corrected immediately (e.g. database corruption).
131pub fn alert(
132 comptime scope: @Type(.EnumLiteral),
133 comptime format: []const u8,
134 args: anytype,
135) void {
136 @setCold(true);
137 log(.alert, scope, format, args);
138}
139
140/// Log a critical message to stderr. This log level is intended to be used
141/// when a bug has been detected or something has gone wrong and it will have
142/// an effect on the operation of the program.
143pub fn crit(
144 comptime scope: @Type(.EnumLiteral),
145 comptime format: []const u8,
146 args: anytype,
147) void {
148 @setCold(true);
149 log(.crit, scope, format, args);
150}
151
152/// Log an error message to stderr. This log level is intended to be used when
153/// a bug has been detected or something has gone wrong but it is recoverable.
154pub fn err(
155 comptime scope: @Type(.EnumLiteral),
156 comptime format: []const u8,
157 args: anytype,
158) void {
159 @setCold(true);
160 log(.err, scope, format, args);
161}
162
163/// Log a warning message to stderr. This log level is intended to be used if
164/// it is uncertain whether something has gone wrong or not, but the
165/// circumstances would be worth investigating.
166pub fn warn(
167 comptime scope: @Type(.EnumLiteral),
168 comptime format: []const u8,
169 args: anytype,
170) void {
171 log(.warn, scope, format, args);
172}
173
174/// Log a notice message to stderr. This log level is intended to be used for
175/// non-error but significant conditions.
176pub fn notice(
177 comptime scope: @Type(.EnumLiteral),
178 comptime format: []const u8,
179 args: anytype,
180) void {
181 log(.notice, scope, format, args);
182}
183
184/// Log an info message to stderr. This log level is intended to be used for
185/// general messages about the state of the program.
186pub fn info(
187 comptime scope: @Type(.EnumLiteral),
188 comptime format: []const u8,
189 args: anytype,
190) void {
191 log(.info, scope, format, args);
192}
193
194/// Log a debug message to stderr. This log level is intended to be used for
195/// messages which are only useful for debugging.
196pub fn debug(
197 comptime scope: @Type(.EnumLiteral),
198 comptime format: []const u8,
199 args: anytype,
200) void {
201 log(.debug, scope, format, args);
202}
lib/std/math.zig+23-23
...@@ -104,7 +104,7 @@ pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool {...@@ -104,7 +104,7 @@ pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool {
104}104}
105105
106// TODO: Hide the following in an internal module.106// TODO: Hide the following in an internal module.
107pub fn forceEval(value: var) void {107pub fn forceEval(value: anytype) void {
108 const T = @TypeOf(value);108 const T = @TypeOf(value);
109 switch (T) {109 switch (T) {
110 f16 => {110 f16 => {
...@@ -122,6 +122,11 @@ pub fn forceEval(value: var) void {...@@ -122,6 +122,11 @@ pub fn forceEval(value: var) void {
122 const p = @ptrCast(*volatile f64, &x);122 const p = @ptrCast(*volatile f64, &x);
123 p.* = x;123 p.* = x;
124 },124 },
125 f128 => {
126 var x: f128 = undefined;
127 const p = @ptrCast(*volatile f128, &x);
128 p.* = x;
129 },
125 else => {130 else => {
126 @compileError("forceEval not implemented for " ++ @typeName(T));131 @compileError("forceEval not implemented for " ++ @typeName(T));
127 },132 },
...@@ -254,7 +259,7 @@ pub fn Min(comptime A: type, comptime B: type) type {...@@ -254,7 +259,7 @@ pub fn Min(comptime A: type, comptime B: type) type {
254259
255/// Returns the smaller number. When one of the parameter's type's full range fits in the other,260/// Returns the smaller number. When one of the parameter's type's full range fits in the other,
256/// the return type is the smaller type.261/// the return type is the smaller type.
257pub fn min(x: var, y: var) Min(@TypeOf(x), @TypeOf(y)) {262pub fn min(x: anytype, y: anytype) Min(@TypeOf(x), @TypeOf(y)) {
258 const Result = Min(@TypeOf(x), @TypeOf(y));263 const Result = Min(@TypeOf(x), @TypeOf(y));
259 if (x < y) {264 if (x < y) {
260 // TODO Zig should allow this as an implicit cast because x is immutable and in this265 // TODO Zig should allow this as an implicit cast because x is immutable and in this
...@@ -305,7 +310,7 @@ test "math.min" {...@@ -305,7 +310,7 @@ test "math.min" {
305 }310 }
306}311}
307312
308pub fn max(x: var, y: var) @TypeOf(x, y) {313pub fn max(x: anytype, y: anytype) @TypeOf(x, y) {
309 return if (x > y) x else y;314 return if (x > y) x else y;
310}315}
311316
...@@ -313,7 +318,7 @@ test "math.max" {...@@ -313,7 +318,7 @@ test "math.max" {
313 testing.expect(max(@as(i32, -1), @as(i32, 2)) == 2);318 testing.expect(max(@as(i32, -1), @as(i32, 2)) == 2);
314}319}
315320
316pub fn clamp(val: var, lower: var, upper: var) @TypeOf(val, lower, upper) {321pub fn clamp(val: anytype, lower: anytype, upper: anytype) @TypeOf(val, lower, upper) {
317 assert(lower <= upper);322 assert(lower <= upper);
318 return max(lower, min(val, upper));323 return max(lower, min(val, upper));
319}324}
...@@ -349,7 +354,7 @@ pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) {...@@ -349,7 +354,7 @@ pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) {
349 return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer;354 return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer;
350}355}
351356
352pub fn negate(x: var) !@TypeOf(x) {357pub fn negate(x: anytype) !@TypeOf(x) {
353 return sub(@TypeOf(x), 0, x);358 return sub(@TypeOf(x), 0, x);
354}359}
355360
...@@ -360,7 +365,7 @@ pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {...@@ -360,7 +365,7 @@ pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T {
360365
361/// Shifts left. Overflowed bits are truncated.366/// Shifts left. Overflowed bits are truncated.
362/// A negative shift amount results in a right shift.367/// A negative shift amount results in a right shift.
363pub fn shl(comptime T: type, a: T, shift_amt: var) T {368pub fn shl(comptime T: type, a: T, shift_amt: anytype) T {
364 const abs_shift_amt = absCast(shift_amt);369 const abs_shift_amt = absCast(shift_amt);
365 const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt);370 const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt);
366371
...@@ -386,7 +391,7 @@ test "math.shl" {...@@ -386,7 +391,7 @@ test "math.shl" {
386391
387/// Shifts right. Overflowed bits are truncated.392/// Shifts right. Overflowed bits are truncated.
388/// A negative shift amount results in a left shift.393/// A negative shift amount results in a left shift.
389pub fn shr(comptime T: type, a: T, shift_amt: var) T {394pub fn shr(comptime T: type, a: T, shift_amt: anytype) T {
390 const abs_shift_amt = absCast(shift_amt);395 const abs_shift_amt = absCast(shift_amt);
391 const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt);396 const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt);
392397
...@@ -414,7 +419,7 @@ test "math.shr" {...@@ -414,7 +419,7 @@ test "math.shr" {
414419
415/// Rotates right. Only unsigned values can be rotated.420/// Rotates right. Only unsigned values can be rotated.
416/// Negative shift values results in shift modulo the bit count.421/// Negative shift values results in shift modulo the bit count.
417pub fn rotr(comptime T: type, x: T, r: var) T {422pub fn rotr(comptime T: type, x: T, r: anytype) T {
418 if (T.is_signed) {423 if (T.is_signed) {
419 @compileError("cannot rotate signed integer");424 @compileError("cannot rotate signed integer");
420 } else {425 } else {
...@@ -433,7 +438,7 @@ test "math.rotr" {...@@ -433,7 +438,7 @@ test "math.rotr" {
433438
434/// Rotates left. Only unsigned values can be rotated.439/// Rotates left. Only unsigned values can be rotated.
435/// Negative shift values results in shift modulo the bit count.440/// Negative shift values results in shift modulo the bit count.
436pub fn rotl(comptime T: type, x: T, r: var) T {441pub fn rotl(comptime T: type, x: T, r: anytype) T {
437 if (T.is_signed) {442 if (T.is_signed) {
438 @compileError("cannot rotate signed integer");443 @compileError("cannot rotate signed integer");
439 } else {444 } else {
...@@ -536,7 +541,7 @@ fn testOverflow() void {...@@ -536,7 +541,7 @@ fn testOverflow() void {
536 testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000);541 testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000);
537}542}
538543
539pub fn absInt(x: var) !@TypeOf(x) {544pub fn absInt(x: anytype) !@TypeOf(x) {
540 const T = @TypeOf(x);545 const T = @TypeOf(x);
541 comptime assert(@typeInfo(T) == .Int); // must pass an integer to absInt546 comptime assert(@typeInfo(T) == .Int); // must pass an integer to absInt
542 comptime assert(T.is_signed); // must pass a signed integer to absInt547 comptime assert(T.is_signed); // must pass a signed integer to absInt
...@@ -684,7 +689,7 @@ fn testRem() void {...@@ -684,7 +689,7 @@ fn testRem() void {
684689
685/// Returns the absolute value of the integer parameter.690/// Returns the absolute value of the integer parameter.
686/// Result is an unsigned integer.691/// Result is an unsigned integer.
687pub fn absCast(x: var) switch (@typeInfo(@TypeOf(x))) {692pub fn absCast(x: anytype) switch (@typeInfo(@TypeOf(x))) {
688 .ComptimeInt => comptime_int,693 .ComptimeInt => comptime_int,
689 .Int => |intInfo| std.meta.Int(false, intInfo.bits),694 .Int => |intInfo| std.meta.Int(false, intInfo.bits),
690 else => @compileError("absCast only accepts integers"),695 else => @compileError("absCast only accepts integers"),
...@@ -719,7 +724,7 @@ test "math.absCast" {...@@ -719,7 +724,7 @@ test "math.absCast" {
719724
720/// Returns the negation of the integer parameter.725/// Returns the negation of the integer parameter.
721/// Result is a signed integer.726/// Result is a signed integer.
722pub fn negateCast(x: var) !std.meta.Int(true, @TypeOf(x).bit_count) {727pub fn negateCast(x: anytype) !std.meta.Int(true, @TypeOf(x).bit_count) {
723 if (@TypeOf(x).is_signed) return negate(x);728 if (@TypeOf(x).is_signed) return negate(x);
724729
725 const int = std.meta.Int(true, @TypeOf(x).bit_count);730 const int = std.meta.Int(true, @TypeOf(x).bit_count);
...@@ -742,7 +747,7 @@ test "math.negateCast" {...@@ -742,7 +747,7 @@ test "math.negateCast" {
742747
743/// Cast an integer to a different integer type. If the value doesn't fit,748/// Cast an integer to a different integer type. If the value doesn't fit,
744/// return an error.749/// return an error.
745pub fn cast(comptime T: type, x: var) (error{Overflow}!T) {750pub fn cast(comptime T: type, x: anytype) (error{Overflow}!T) {
746 comptime assert(@typeInfo(T) == .Int); // must pass an integer751 comptime assert(@typeInfo(T) == .Int); // must pass an integer
747 comptime assert(@typeInfo(@TypeOf(x)) == .Int); // must pass an integer752 comptime assert(@typeInfo(@TypeOf(x)) == .Int); // must pass an integer
748 if (maxInt(@TypeOf(x)) > maxInt(T) and x > maxInt(T)) {753 if (maxInt(@TypeOf(x)) > maxInt(T) and x > maxInt(T)) {
...@@ -767,7 +772,7 @@ test "math.cast" {...@@ -767,7 +772,7 @@ test "math.cast" {
767pub const AlignCastError = error{UnalignedMemory};772pub const AlignCastError = error{UnalignedMemory};
768773
769/// Align cast a pointer but return an error if it's the wrong alignment774/// Align cast a pointer but return an error if it's the wrong alignment
770pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@TypeOf(@alignCast(alignment, ptr)) {775pub fn alignCast(comptime alignment: u29, ptr: anytype) AlignCastError!@TypeOf(@alignCast(alignment, ptr)) {
771 const addr = @ptrToInt(ptr);776 const addr = @ptrToInt(ptr);
772 if (addr % alignment != 0) {777 if (addr % alignment != 0) {
773 return error.UnalignedMemory;778 return error.UnalignedMemory;
...@@ -775,7 +780,7 @@ pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@TypeOf(@alig...@@ -775,7 +780,7 @@ pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@TypeOf(@alig
775 return @alignCast(alignment, ptr);780 return @alignCast(alignment, ptr);
776}781}
777782
778pub fn isPowerOfTwo(v: var) bool {783pub fn isPowerOfTwo(v: anytype) bool {
779 assert(v != 0);784 assert(v != 0);
780 return (v & (v - 1)) == 0;785 return (v & (v - 1)) == 0;
781}786}
...@@ -892,7 +897,7 @@ test "std.math.log2_int_ceil" {...@@ -892,7 +897,7 @@ test "std.math.log2_int_ceil" {
892 testing.expect(log2_int_ceil(u32, 10) == 4);897 testing.expect(log2_int_ceil(u32, 10) == 4);
893}898}
894899
895pub fn lossyCast(comptime T: type, value: var) T {900pub fn lossyCast(comptime T: type, value: anytype) T {
896 switch (@typeInfo(@TypeOf(value))) {901 switch (@typeInfo(@TypeOf(value))) {
897 .Int => return @intToFloat(T, value),902 .Int => return @intToFloat(T, value),
898 .Float => return @floatCast(T, value),903 .Float => return @floatCast(T, value),
...@@ -1026,7 +1031,7 @@ pub const Order = enum {...@@ -1026,7 +1031,7 @@ pub const Order = enum {
1026};1031};
10271032
1028/// Given two numbers, this function returns the order they are with respect to each other.1033/// Given two numbers, this function returns the order they are with respect to each other.
1029pub fn order(a: var, b: var) Order {1034pub fn order(a: anytype, b: anytype) Order {
1030 if (a == b) {1035 if (a == b) {
1031 return .eq;1036 return .eq;
1032 } else if (a < b) {1037 } else if (a < b) {
...@@ -1042,19 +1047,14 @@ pub fn order(a: var, b: var) Order {...@@ -1042,19 +1047,14 @@ pub fn order(a: var, b: var) Order {
1042pub const CompareOperator = enum {1047pub const CompareOperator = enum {
1043 /// Less than (`<`)1048 /// Less than (`<`)
1044 lt,1049 lt,
1045
1046 /// Less than or equal (`<=`)1050 /// Less than or equal (`<=`)
1047 lte,1051 lte,
1048
1049 /// Equal (`==`)1052 /// Equal (`==`)
1050 eq,1053 eq,
1051
1052 /// Greater than or equal (`>=`)1054 /// Greater than or equal (`>=`)
1053 gte,1055 gte,
1054
1055 /// Greater than (`>`)1056 /// Greater than (`>`)
1056 gt,1057 gt,
1057
1058 /// Not equal (`!=`)1058 /// Not equal (`!=`)
1059 neq,1059 neq,
1060};1060};
...@@ -1062,7 +1062,7 @@ pub const CompareOperator = enum {...@@ -1062,7 +1062,7 @@ pub const CompareOperator = enum {
1062/// This function does the same thing as comparison operators, however the1062/// This function does the same thing as comparison operators, however the
1063/// operator is a runtime-known enum value. Works on any operands that1063/// operator is a runtime-known enum value. Works on any operands that
1064/// support comparison operators.1064/// support comparison operators.
1065pub fn compare(a: var, op: CompareOperator, b: var) bool {1065pub fn compare(a: anytype, op: CompareOperator, b: anytype) bool {
1066 return switch (op) {1066 return switch (op) {
1067 .lt => a < b,1067 .lt => a < b,
1068 .lte => a <= b,1068 .lte => a <= b,
lib/std/math/acos.zig+1-1
...@@ -12,7 +12,7 @@ const expect = std.testing.expect;...@@ -12,7 +12,7 @@ const expect = std.testing.expect;
12///12///
13/// Special cases:13/// Special cases:
14/// - acos(x) = nan if x < -1 or x > 114/// - acos(x) = nan if x < -1 or x > 1
15pub fn acos(x: var) @TypeOf(x) {15pub fn acos(x: anytype) @TypeOf(x) {
16 const T = @TypeOf(x);16 const T = @TypeOf(x);
17 return switch (T) {17 return switch (T) {
18 f32 => acos32(x),18 f32 => acos32(x),
lib/std/math/acosh.zig+1-1
...@@ -14,7 +14,7 @@ const expect = std.testing.expect;...@@ -14,7 +14,7 @@ const expect = std.testing.expect;
14/// Special cases:14/// Special cases:
15/// - acosh(x) = snan if x < 115/// - acosh(x) = snan if x < 1
16/// - acosh(nan) = nan16/// - acosh(nan) = nan
17pub fn acosh(x: var) @TypeOf(x) {17pub fn acosh(x: anytype) @TypeOf(x) {
18 const T = @TypeOf(x);18 const T = @TypeOf(x);
19 return switch (T) {19 return switch (T) {
20 f32 => acosh32(x),20 f32 => acosh32(x),
lib/std/math/asin.zig+1-1
...@@ -13,7 +13,7 @@ const expect = std.testing.expect;...@@ -13,7 +13,7 @@ const expect = std.testing.expect;
13/// Special Cases:13/// Special Cases:
14/// - asin(+-0) = +-014/// - asin(+-0) = +-0
15/// - asin(x) = nan if x < -1 or x > 115/// - asin(x) = nan if x < -1 or x > 1
16pub fn asin(x: var) @TypeOf(x) {16pub fn asin(x: anytype) @TypeOf(x) {
17 const T = @TypeOf(x);17 const T = @TypeOf(x);
18 return switch (T) {18 return switch (T) {
19 f32 => asin32(x),19 f32 => asin32(x),
lib/std/math/asinh.zig+1-1
...@@ -15,7 +15,7 @@ const maxInt = std.math.maxInt;...@@ -15,7 +15,7 @@ const maxInt = std.math.maxInt;
15/// - asinh(+-0) = +-015/// - asinh(+-0) = +-0
16/// - asinh(+-inf) = +-inf16/// - asinh(+-inf) = +-inf
17/// - asinh(nan) = nan17/// - asinh(nan) = nan
18pub fn asinh(x: var) @TypeOf(x) {18pub fn asinh(x: anytype) @TypeOf(x) {
19 const T = @TypeOf(x);19 const T = @TypeOf(x);
20 return switch (T) {20 return switch (T) {
21 f32 => asinh32(x),21 f32 => asinh32(x),
lib/std/math/atan.zig+1-1
...@@ -13,7 +13,7 @@ const expect = std.testing.expect;...@@ -13,7 +13,7 @@ const expect = std.testing.expect;
13/// Special Cases:13/// Special Cases:
14/// - atan(+-0) = +-014/// - atan(+-0) = +-0
15/// - atan(+-inf) = +-pi/215/// - atan(+-inf) = +-pi/2
16pub fn atan(x: var) @TypeOf(x) {16pub fn atan(x: anytype) @TypeOf(x) {
17 const T = @TypeOf(x);17 const T = @TypeOf(x);
18 return switch (T) {18 return switch (T) {
19 f32 => atan32(x),19 f32 => atan32(x),
lib/std/math/atanh.zig+1-1
...@@ -15,7 +15,7 @@ const maxInt = std.math.maxInt;...@@ -15,7 +15,7 @@ const maxInt = std.math.maxInt;
15/// - atanh(+-1) = +-inf with signal15/// - atanh(+-1) = +-inf with signal
16/// - atanh(x) = nan if |x| > 1 with signal16/// - atanh(x) = nan if |x| > 1 with signal
17/// - atanh(nan) = nan17/// - atanh(nan) = nan
18pub fn atanh(x: var) @TypeOf(x) {18pub fn atanh(x: anytype) @TypeOf(x) {
19 const T = @TypeOf(x);19 const T = @TypeOf(x);
20 return switch (T) {20 return switch (T) {
21 f32 => atanh_32(x),21 f32 => atanh_32(x),
lib/std/math/big/int.zig+11-11
...@@ -12,7 +12,7 @@ const assert = std.debug.assert;...@@ -12,7 +12,7 @@ const assert = std.debug.assert;
1212
13/// Returns the number of limbs needed to store `scalar`, which must be a13/// Returns the number of limbs needed to store `scalar`, which must be a
14/// primitive integer value.14/// primitive integer value.
15pub fn calcLimbLen(scalar: var) usize {15pub fn calcLimbLen(scalar: anytype) usize {
16 const T = @TypeOf(scalar);16 const T = @TypeOf(scalar);
17 switch (@typeInfo(T)) {17 switch (@typeInfo(T)) {
18 .Int => |info| {18 .Int => |info| {
...@@ -110,7 +110,7 @@ pub const Mutable = struct {...@@ -110,7 +110,7 @@ pub const Mutable = struct {
110 /// `value` is a primitive integer type.110 /// `value` is a primitive integer type.
111 /// Asserts the value fits within the provided `limbs_buffer`.111 /// Asserts the value fits within the provided `limbs_buffer`.
112 /// Note: `calcLimbLen` can be used to figure out how big an array to allocate for `limbs_buffer`.112 /// Note: `calcLimbLen` can be used to figure out how big an array to allocate for `limbs_buffer`.
113 pub fn init(limbs_buffer: []Limb, value: var) Mutable {113 pub fn init(limbs_buffer: []Limb, value: anytype) Mutable {
114 limbs_buffer[0] = 0;114 limbs_buffer[0] = 0;
115 var self: Mutable = .{115 var self: Mutable = .{
116 .limbs = limbs_buffer,116 .limbs = limbs_buffer,
...@@ -169,7 +169,7 @@ pub const Mutable = struct {...@@ -169,7 +169,7 @@ pub const Mutable = struct {
169 /// Asserts the value fits within the limbs buffer.169 /// Asserts the value fits within the limbs buffer.
170 /// Note: `calcLimbLen` can be used to figure out how big the limbs buffer170 /// Note: `calcLimbLen` can be used to figure out how big the limbs buffer
171 /// needs to be to store a specific value.171 /// needs to be to store a specific value.
172 pub fn set(self: *Mutable, value: var) void {172 pub fn set(self: *Mutable, value: anytype) void {
173 const T = @TypeOf(value);173 const T = @TypeOf(value);
174174
175 switch (@typeInfo(T)) {175 switch (@typeInfo(T)) {
...@@ -281,7 +281,7 @@ pub const Mutable = struct {...@@ -281,7 +281,7 @@ pub const Mutable = struct {
281 ///281 ///
282 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by282 /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
283 /// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`.283 /// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`.
284 pub fn addScalar(r: *Mutable, a: Const, scalar: var) void {284 pub fn addScalar(r: *Mutable, a: Const, scalar: anytype) void {
285 var limbs: [calcLimbLen(scalar)]Limb = undefined;285 var limbs: [calcLimbLen(scalar)]Limb = undefined;
286 const operand = init(&limbs, scalar).toConst();286 const operand = init(&limbs, scalar).toConst();
287 return add(r, a, operand);287 return add(r, a, operand);
...@@ -1058,7 +1058,7 @@ pub const Const = struct {...@@ -1058,7 +1058,7 @@ pub const Const = struct {
1058 self: Const,1058 self: Const,
1059 comptime fmt: []const u8,1059 comptime fmt: []const u8,
1060 options: std.fmt.FormatOptions,1060 options: std.fmt.FormatOptions,
1061 out_stream: var,1061 out_stream: anytype,
1062 ) !void {1062 ) !void {
1063 comptime var radix = 10;1063 comptime var radix = 10;
1064 comptime var uppercase = false;1064 comptime var uppercase = false;
...@@ -1105,7 +1105,7 @@ pub const Const = struct {...@@ -1105,7 +1105,7 @@ pub const Const = struct {
1105 assert(base <= 16);1105 assert(base <= 16);
11061106
1107 if (self.eqZero()) {1107 if (self.eqZero()) {
1108 return mem.dupe(allocator, u8, "0");1108 return allocator.dupe(u8, "0");
1109 }1109 }
1110 const string = try allocator.alloc(u8, self.sizeInBaseUpperBound(base));1110 const string = try allocator.alloc(u8, self.sizeInBaseUpperBound(base));
1111 errdefer allocator.free(string);1111 errdefer allocator.free(string);
...@@ -1261,7 +1261,7 @@ pub const Const = struct {...@@ -1261,7 +1261,7 @@ pub const Const = struct {
1261 }1261 }
12621262
1263 /// Same as `order` but the right-hand operand is a primitive integer.1263 /// Same as `order` but the right-hand operand is a primitive integer.
1264 pub fn orderAgainstScalar(lhs: Const, scalar: var) math.Order {1264 pub fn orderAgainstScalar(lhs: Const, scalar: anytype) math.Order {
1265 var limbs: [calcLimbLen(scalar)]Limb = undefined;1265 var limbs: [calcLimbLen(scalar)]Limb = undefined;
1266 const rhs = Mutable.init(&limbs, scalar);1266 const rhs = Mutable.init(&limbs, scalar);
1267 return order(lhs, rhs.toConst());1267 return order(lhs, rhs.toConst());
...@@ -1333,7 +1333,7 @@ pub const Managed = struct {...@@ -1333,7 +1333,7 @@ pub const Managed = struct {
1333 /// Creates a new `Managed` with value `value`.1333 /// Creates a new `Managed` with value `value`.
1334 ///1334 ///
1335 /// This is identical to an `init`, followed by a `set`.1335 /// This is identical to an `init`, followed by a `set`.
1336 pub fn initSet(allocator: *Allocator, value: var) !Managed {1336 pub fn initSet(allocator: *Allocator, value: anytype) !Managed {
1337 var s = try Managed.init(allocator);1337 var s = try Managed.init(allocator);
1338 try s.set(value);1338 try s.set(value);
1339 return s;1339 return s;
...@@ -1496,7 +1496,7 @@ pub const Managed = struct {...@@ -1496,7 +1496,7 @@ pub const Managed = struct {
1496 }1496 }
14971497
1498 /// Sets an Managed to value. Value must be an primitive integer type.1498 /// Sets an Managed to value. Value must be an primitive integer type.
1499 pub fn set(self: *Managed, value: var) Allocator.Error!void {1499 pub fn set(self: *Managed, value: anytype) Allocator.Error!void {
1500 try self.ensureCapacity(calcLimbLen(value));1500 try self.ensureCapacity(calcLimbLen(value));
1501 var m = self.toMutable();1501 var m = self.toMutable();
1502 m.set(value);1502 m.set(value);
...@@ -1549,7 +1549,7 @@ pub const Managed = struct {...@@ -1549,7 +1549,7 @@ pub const Managed = struct {
1549 self: Managed,1549 self: Managed,
1550 comptime fmt: []const u8,1550 comptime fmt: []const u8,
1551 options: std.fmt.FormatOptions,1551 options: std.fmt.FormatOptions,
1552 out_stream: var,1552 out_stream: anytype,
1553 ) !void {1553 ) !void {
1554 return self.toConst().format(fmt, options, out_stream);1554 return self.toConst().format(fmt, options, out_stream);
1555 }1555 }
...@@ -1607,7 +1607,7 @@ pub const Managed = struct {...@@ -1607,7 +1607,7 @@ pub const Managed = struct {
1607 /// scalar is a primitive integer type.1607 /// scalar is a primitive integer type.
1608 ///1608 ///
1609 /// Returns an error if memory could not be allocated.1609 /// Returns an error if memory could not be allocated.
1610 pub fn addScalar(r: *Managed, a: Const, scalar: var) Allocator.Error!void {1610 pub fn addScalar(r: *Managed, a: Const, scalar: anytype) Allocator.Error!void {
1611 try r.ensureCapacity(math.max(a.limbs.len, calcLimbLen(scalar)) + 1);1611 try r.ensureCapacity(math.max(a.limbs.len, calcLimbLen(scalar)) + 1);
1612 var m = r.toMutable();1612 var m = r.toMutable();
1613 m.addScalar(a, scalar);1613 m.addScalar(a, scalar);
lib/std/math/big/rational.zig+2-2
...@@ -43,7 +43,7 @@ pub const Rational = struct {...@@ -43,7 +43,7 @@ pub const Rational = struct {
43 }43 }
4444
45 /// Set a Rational from a primitive integer type.45 /// Set a Rational from a primitive integer type.
46 pub fn setInt(self: *Rational, a: var) !void {46 pub fn setInt(self: *Rational, a: anytype) !void {
47 try self.p.set(a);47 try self.p.set(a);
48 try self.q.set(1);48 try self.q.set(1);
49 }49 }
...@@ -280,7 +280,7 @@ pub const Rational = struct {...@@ -280,7 +280,7 @@ pub const Rational = struct {
280 }280 }
281281
282 /// Set a rational from an integer ratio.282 /// Set a rational from an integer ratio.
283 pub fn setRatio(self: *Rational, p: var, q: var) !void {283 pub fn setRatio(self: *Rational, p: anytype, q: anytype) !void {
284 try self.p.set(p);284 try self.p.set(p);
285 try self.q.set(q);285 try self.q.set(q);
286286
lib/std/math/cbrt.zig+1-1
...@@ -14,7 +14,7 @@ const expect = std.testing.expect;...@@ -14,7 +14,7 @@ const expect = std.testing.expect;
14/// - cbrt(+-0) = +-014/// - cbrt(+-0) = +-0
15/// - cbrt(+-inf) = +-inf15/// - cbrt(+-inf) = +-inf
16/// - cbrt(nan) = nan16/// - cbrt(nan) = nan
17pub fn cbrt(x: var) @TypeOf(x) {17pub fn cbrt(x: anytype) @TypeOf(x) {
18 const T = @TypeOf(x);18 const T = @TypeOf(x);
19 return switch (T) {19 return switch (T) {
20 f32 => cbrt32(x),20 f32 => cbrt32(x),
lib/std/math/ceil.zig+44-1
...@@ -15,11 +15,12 @@ const expect = std.testing.expect;...@@ -15,11 +15,12 @@ const expect = std.testing.expect;
15/// - ceil(+-0) = +-015/// - ceil(+-0) = +-0
16/// - ceil(+-inf) = +-inf16/// - ceil(+-inf) = +-inf
17/// - ceil(nan) = nan17/// - ceil(nan) = nan
18pub fn ceil(x: var) @TypeOf(x) {18pub fn ceil(x: anytype) @TypeOf(x) {
19 const T = @TypeOf(x);19 const T = @TypeOf(x);
20 return switch (T) {20 return switch (T) {
21 f32 => ceil32(x),21 f32 => ceil32(x),
22 f64 => ceil64(x),22 f64 => ceil64(x),
23 f128 => ceil128(x),
23 else => @compileError("ceil not implemented for " ++ @typeName(T)),24 else => @compileError("ceil not implemented for " ++ @typeName(T)),
24 };25 };
25}26}
...@@ -86,9 +87,37 @@ fn ceil64(x: f64) f64 {...@@ -86,9 +87,37 @@ fn ceil64(x: f64) f64 {
86 }87 }
87}88}
8889
90fn ceil128(x: f128) f128 {
91 const u = @bitCast(u128, x);
92 const e = (u >> 112) & 0x7FFF;
93 var y: f128 = undefined;
94
95 if (e >= 0x3FFF + 112 or x == 0) return x;
96
97 if (u >> 127 != 0) {
98 y = x - math.f128_toint + math.f128_toint - x;
99 } else {
100 y = x + math.f128_toint - math.f128_toint - x;
101 }
102
103 if (e <= 0x3FFF - 1) {
104 math.forceEval(y);
105 if (u >> 127 != 0) {
106 return -0.0;
107 } else {
108 return 1.0;
109 }
110 } else if (y < 0) {
111 return x + y + 1;
112 } else {
113 return x + y;
114 }
115}
116
89test "math.ceil" {117test "math.ceil" {
90 expect(ceil(@as(f32, 0.0)) == ceil32(0.0));118 expect(ceil(@as(f32, 0.0)) == ceil32(0.0));
91 expect(ceil(@as(f64, 0.0)) == ceil64(0.0));119 expect(ceil(@as(f64, 0.0)) == ceil64(0.0));
120 expect(ceil(@as(f128, 0.0)) == ceil128(0.0));
92}121}
93122
94test "math.ceil32" {123test "math.ceil32" {
...@@ -103,6 +132,12 @@ test "math.ceil64" {...@@ -103,6 +132,12 @@ test "math.ceil64" {
103 expect(ceil64(0.2) == 1.0);132 expect(ceil64(0.2) == 1.0);
104}133}
105134
135test "math.ceil128" {
136 expect(ceil128(1.3) == 2.0);
137 expect(ceil128(-1.3) == -1.0);
138 expect(ceil128(0.2) == 1.0);
139}
140
106test "math.ceil32.special" {141test "math.ceil32.special" {
107 expect(ceil32(0.0) == 0.0);142 expect(ceil32(0.0) == 0.0);
108 expect(ceil32(-0.0) == -0.0);143 expect(ceil32(-0.0) == -0.0);
...@@ -118,3 +153,11 @@ test "math.ceil64.special" {...@@ -118,3 +153,11 @@ test "math.ceil64.special" {
118 expect(math.isNegativeInf(ceil64(-math.inf(f64))));153 expect(math.isNegativeInf(ceil64(-math.inf(f64))));
119 expect(math.isNan(ceil64(math.nan(f64))));154 expect(math.isNan(ceil64(math.nan(f64))));
120}155}
156
157test "math.ceil128.special" {
158 expect(ceil128(0.0) == 0.0);
159 expect(ceil128(-0.0) == -0.0);
160 expect(math.isPositiveInf(ceil128(math.inf(f128))));
161 expect(math.isNegativeInf(ceil128(-math.inf(f128))));
162 expect(math.isNan(ceil128(math.nan(f128))));
163}
lib/std/math/complex/abs.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the absolute value (modulus) of z.7/// Returns the absolute value (modulus) of z.
8pub fn abs(z: var) @TypeOf(z.re) {8pub fn abs(z: anytype) @TypeOf(z.re) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 return math.hypot(T, z.re, z.im);10 return math.hypot(T, z.re, z.im);
11}11}
lib/std/math/complex/acos.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the arc-cosine of z.7/// Returns the arc-cosine of z.
8pub fn acos(z: var) Complex(@TypeOf(z.re)) {8pub fn acos(z: anytype) Complex(@TypeOf(z.re)) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 const q = cmath.asin(z);10 const q = cmath.asin(z);
11 return Complex(T).new(@as(T, math.pi) / 2 - q.re, -q.im);11 return Complex(T).new(@as(T, math.pi) / 2 - q.re, -q.im);
lib/std/math/complex/acosh.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the hyperbolic arc-cosine of z.7/// Returns the hyperbolic arc-cosine of z.
8pub fn acosh(z: var) Complex(@TypeOf(z.re)) {8pub fn acosh(z: anytype) Complex(@TypeOf(z.re)) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 const q = cmath.acos(z);10 const q = cmath.acos(z);
11 return Complex(T).new(-q.im, q.re);11 return Complex(T).new(-q.im, q.re);
lib/std/math/complex/arg.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the angular component (in radians) of z.7/// Returns the angular component (in radians) of z.
8pub fn arg(z: var) @TypeOf(z.re) {8pub fn arg(z: anytype) @TypeOf(z.re) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 return math.atan2(T, z.im, z.re);10 return math.atan2(T, z.im, z.re);
11}11}
lib/std/math/complex/asin.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7// Returns the arc-sine of z.7// Returns the arc-sine of z.
8pub fn asin(z: var) Complex(@TypeOf(z.re)) {8pub fn asin(z: anytype) Complex(@TypeOf(z.re)) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 const x = z.re;10 const x = z.re;
11 const y = z.im;11 const y = z.im;
lib/std/math/complex/asinh.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the hyperbolic arc-sine of z.7/// Returns the hyperbolic arc-sine of z.
8pub fn asinh(z: var) Complex(@TypeOf(z.re)) {8pub fn asinh(z: anytype) Complex(@TypeOf(z.re)) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 const q = Complex(T).new(-z.im, z.re);10 const q = Complex(T).new(-z.im, z.re);
11 const r = cmath.asin(q);11 const r = cmath.asin(q);
lib/std/math/complex/atan.zig+1-1
...@@ -12,7 +12,7 @@ const cmath = math.complex;...@@ -12,7 +12,7 @@ const cmath = math.complex;
12const Complex = cmath.Complex;12const Complex = cmath.Complex;
1313
14/// Returns the arc-tangent of z.14/// Returns the arc-tangent of z.
15pub fn atan(z: var) @TypeOf(z) {15pub fn atan(z: anytype) @TypeOf(z) {
16 const T = @TypeOf(z.re);16 const T = @TypeOf(z.re);
17 return switch (T) {17 return switch (T) {
18 f32 => atan32(z),18 f32 => atan32(z),
lib/std/math/complex/atanh.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the hyperbolic arc-tangent of z.7/// Returns the hyperbolic arc-tangent of z.
8pub fn atanh(z: var) Complex(@TypeOf(z.re)) {8pub fn atanh(z: anytype) Complex(@TypeOf(z.re)) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 const q = Complex(T).new(-z.im, z.re);10 const q = Complex(T).new(-z.im, z.re);
11 const r = cmath.atan(q);11 const r = cmath.atan(q);
lib/std/math/complex/conj.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the complex conjugate of z.7/// Returns the complex conjugate of z.
8pub fn conj(z: var) Complex(@TypeOf(z.re)) {8pub fn conj(z: anytype) Complex(@TypeOf(z.re)) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 return Complex(T).new(z.re, -z.im);10 return Complex(T).new(z.re, -z.im);
11}11}
lib/std/math/complex/cos.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the cosine of z.7/// Returns the cosine of z.
8pub fn cos(z: var) Complex(@TypeOf(z.re)) {8pub fn cos(z: anytype) Complex(@TypeOf(z.re)) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 const p = Complex(T).new(-z.im, z.re);10 const p = Complex(T).new(-z.im, z.re);
11 return cmath.cosh(p);11 return cmath.cosh(p);
lib/std/math/complex/cosh.zig+1-1
...@@ -14,7 +14,7 @@ const Complex = cmath.Complex;...@@ -14,7 +14,7 @@ const Complex = cmath.Complex;
14const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;14const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
1515
16/// Returns the hyperbolic arc-cosine of z.16/// Returns the hyperbolic arc-cosine of z.
17pub fn cosh(z: var) Complex(@TypeOf(z.re)) {17pub fn cosh(z: anytype) Complex(@TypeOf(z.re)) {
18 const T = @TypeOf(z.re);18 const T = @TypeOf(z.re);
19 return switch (T) {19 return switch (T) {
20 f32 => cosh32(z),20 f32 => cosh32(z),
lib/std/math/complex/exp.zig+1-1
...@@ -14,7 +14,7 @@ const Complex = cmath.Complex;...@@ -14,7 +14,7 @@ const Complex = cmath.Complex;
14const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;14const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
1515
16/// Returns e raised to the power of z (e^z).16/// Returns e raised to the power of z (e^z).
17pub fn exp(z: var) @TypeOf(z) {17pub fn exp(z: anytype) @TypeOf(z) {
18 const T = @TypeOf(z.re);18 const T = @TypeOf(z.re);
1919
20 return switch (T) {20 return switch (T) {
lib/std/math/complex/ldexp.zig+1-1
...@@ -11,7 +11,7 @@ const cmath = math.complex;...@@ -11,7 +11,7 @@ const cmath = math.complex;
11const Complex = cmath.Complex;11const Complex = cmath.Complex;
1212
13/// Returns exp(z) scaled to avoid overflow.13/// Returns exp(z) scaled to avoid overflow.
14pub fn ldexp_cexp(z: var, expt: i32) @TypeOf(z) {14pub fn ldexp_cexp(z: anytype, expt: i32) @TypeOf(z) {
15 const T = @TypeOf(z.re);15 const T = @TypeOf(z.re);
1616
17 return switch (T) {17 return switch (T) {
lib/std/math/complex/log.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the natural logarithm of z.7/// Returns the natural logarithm of z.
8pub fn log(z: var) Complex(@TypeOf(z.re)) {8pub fn log(z: anytype) Complex(@TypeOf(z.re)) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 const r = cmath.abs(z);10 const r = cmath.abs(z);
11 const phi = cmath.arg(z);11 const phi = cmath.arg(z);
lib/std/math/complex/proj.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the projection of z onto the riemann sphere.7/// Returns the projection of z onto the riemann sphere.
8pub fn proj(z: var) Complex(@TypeOf(z.re)) {8pub fn proj(z: anytype) Complex(@TypeOf(z.re)) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
1010
11 if (math.isInf(z.re) or math.isInf(z.im)) {11 if (math.isInf(z.re) or math.isInf(z.im)) {
lib/std/math/complex/sin.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the sine of z.7/// Returns the sine of z.
8pub fn sin(z: var) Complex(@TypeOf(z.re)) {8pub fn sin(z: anytype) Complex(@TypeOf(z.re)) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 const p = Complex(T).new(-z.im, z.re);10 const p = Complex(T).new(-z.im, z.re);
11 const q = cmath.sinh(p);11 const q = cmath.sinh(p);
lib/std/math/complex/sinh.zig+1-1
...@@ -14,7 +14,7 @@ const Complex = cmath.Complex;...@@ -14,7 +14,7 @@ const Complex = cmath.Complex;
14const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;14const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
1515
16/// Returns the hyperbolic sine of z.16/// Returns the hyperbolic sine of z.
17pub fn sinh(z: var) @TypeOf(z) {17pub fn sinh(z: anytype) @TypeOf(z) {
18 const T = @TypeOf(z.re);18 const T = @TypeOf(z.re);
19 return switch (T) {19 return switch (T) {
20 f32 => sinh32(z),20 f32 => sinh32(z),
lib/std/math/complex/sqrt.zig+1-1
...@@ -12,7 +12,7 @@ const Complex = cmath.Complex;...@@ -12,7 +12,7 @@ const Complex = cmath.Complex;
1212
13/// Returns the square root of z. The real and imaginary parts of the result have the same sign13/// Returns the square root of z. The real and imaginary parts of the result have the same sign
14/// as the imaginary part of z.14/// as the imaginary part of z.
15pub fn sqrt(z: var) @TypeOf(z) {15pub fn sqrt(z: anytype) @TypeOf(z) {
16 const T = @TypeOf(z.re);16 const T = @TypeOf(z.re);
1717
18 return switch (T) {18 return switch (T) {
lib/std/math/complex/tan.zig+1-1
...@@ -5,7 +5,7 @@ const cmath = math.complex;...@@ -5,7 +5,7 @@ const cmath = math.complex;
5const Complex = cmath.Complex;5const Complex = cmath.Complex;
66
7/// Returns the tanget of z.7/// Returns the tanget of z.
8pub fn tan(z: var) Complex(@TypeOf(z.re)) {8pub fn tan(z: anytype) Complex(@TypeOf(z.re)) {
9 const T = @TypeOf(z.re);9 const T = @TypeOf(z.re);
10 const q = Complex(T).new(-z.im, z.re);10 const q = Complex(T).new(-z.im, z.re);
11 const r = cmath.tanh(q);11 const r = cmath.tanh(q);
lib/std/math/complex/tanh.zig+1-1
...@@ -12,7 +12,7 @@ const cmath = math.complex;...@@ -12,7 +12,7 @@ const cmath = math.complex;
12const Complex = cmath.Complex;12const Complex = cmath.Complex;
1313
14/// Returns the hyperbolic tangent of z.14/// Returns the hyperbolic tangent of z.
15pub fn tanh(z: var) @TypeOf(z) {15pub fn tanh(z: anytype) @TypeOf(z) {
16 const T = @TypeOf(z.re);16 const T = @TypeOf(z.re);
17 return switch (T) {17 return switch (T) {
18 f32 => tanh32(z),18 f32 => tanh32(z),
lib/std/math/cos.zig+1-1
...@@ -13,7 +13,7 @@ const expect = std.testing.expect;...@@ -13,7 +13,7 @@ const expect = std.testing.expect;
13/// Special Cases:13/// Special Cases:
14/// - cos(+-inf) = nan14/// - cos(+-inf) = nan
15/// - cos(nan) = nan15/// - cos(nan) = nan
16pub fn cos(x: var) @TypeOf(x) {16pub fn cos(x: anytype) @TypeOf(x) {
17 const T = @TypeOf(x);17 const T = @TypeOf(x);
18 return switch (T) {18 return switch (T) {
19 f32 => cos_(f32, x),19 f32 => cos_(f32, x),
lib/std/math/cosh.zig+1-1
...@@ -17,7 +17,7 @@ const maxInt = std.math.maxInt;...@@ -17,7 +17,7 @@ const maxInt = std.math.maxInt;
17/// - cosh(+-0) = 117/// - cosh(+-0) = 1
18/// - cosh(+-inf) = +inf18/// - cosh(+-inf) = +inf
19/// - cosh(nan) = nan19/// - cosh(nan) = nan
20pub fn cosh(x: var) @TypeOf(x) {20pub fn cosh(x: anytype) @TypeOf(x) {
21 const T = @TypeOf(x);21 const T = @TypeOf(x);
22 return switch (T) {22 return switch (T) {
23 f32 => cosh32(x),23 f32 => cosh32(x),
lib/std/math/exp.zig+1-1
...@@ -14,7 +14,7 @@ const builtin = @import("builtin");...@@ -14,7 +14,7 @@ const builtin = @import("builtin");
14/// Special Cases:14/// Special Cases:
15/// - exp(+inf) = +inf15/// - exp(+inf) = +inf
16/// - exp(nan) = nan16/// - exp(nan) = nan
17pub fn exp(x: var) @TypeOf(x) {17pub fn exp(x: anytype) @TypeOf(x) {
18 const T = @TypeOf(x);18 const T = @TypeOf(x);
19 return switch (T) {19 return switch (T) {
20 f32 => exp32(x),20 f32 => exp32(x),
lib/std/math/exp2.zig+1-1
...@@ -13,7 +13,7 @@ const expect = std.testing.expect;...@@ -13,7 +13,7 @@ const expect = std.testing.expect;
13/// Special Cases:13/// Special Cases:
14/// - exp2(+inf) = +inf14/// - exp2(+inf) = +inf
15/// - exp2(nan) = nan15/// - exp2(nan) = nan
16pub fn exp2(x: var) @TypeOf(x) {16pub fn exp2(x: anytype) @TypeOf(x) {
17 const T = @TypeOf(x);17 const T = @TypeOf(x);
18 return switch (T) {18 return switch (T) {
19 f32 => exp2_32(x),19 f32 => exp2_32(x),
lib/std/math/expm1.zig+1-1
...@@ -18,7 +18,7 @@ const expect = std.testing.expect;...@@ -18,7 +18,7 @@ const expect = std.testing.expect;
18/// - expm1(+inf) = +inf18/// - expm1(+inf) = +inf
19/// - expm1(-inf) = -119/// - expm1(-inf) = -1
20/// - expm1(nan) = nan20/// - expm1(nan) = nan
21pub fn expm1(x: var) @TypeOf(x) {21pub fn expm1(x: anytype) @TypeOf(x) {
22 const T = @TypeOf(x);22 const T = @TypeOf(x);
23 return switch (T) {23 return switch (T) {
24 f32 => expm1_32(x),24 f32 => expm1_32(x),
lib/std/math/expo2.zig+1-1
...@@ -7,7 +7,7 @@...@@ -7,7 +7,7 @@
7const math = @import("../math.zig");7const math = @import("../math.zig");
88
9/// Returns exp(x) / 2 for x >= log(maxFloat(T)).9/// Returns exp(x) / 2 for x >= log(maxFloat(T)).
10pub fn expo2(x: var) @TypeOf(x) {10pub fn expo2(x: anytype) @TypeOf(x) {
11 const T = @TypeOf(x);11 const T = @TypeOf(x);
12 return switch (T) {12 return switch (T) {
13 f32 => expo2f(x),13 f32 => expo2f(x),
lib/std/math/fabs.zig+1-1
...@@ -14,7 +14,7 @@ const maxInt = std.math.maxInt;...@@ -14,7 +14,7 @@ const maxInt = std.math.maxInt;
14/// Special Cases:14/// Special Cases:
15/// - fabs(+-inf) = +inf15/// - fabs(+-inf) = +inf
16/// - fabs(nan) = nan16/// - fabs(nan) = nan
17pub fn fabs(x: var) @TypeOf(x) {17pub fn fabs(x: anytype) @TypeOf(x) {
18 const T = @TypeOf(x);18 const T = @TypeOf(x);
19 return switch (T) {19 return switch (T) {
20 f16 => fabs16(x),20 f16 => fabs16(x),
lib/std/math/floor.zig+44-1
...@@ -15,12 +15,13 @@ const math = std.math;...@@ -15,12 +15,13 @@ const math = std.math;
15/// - floor(+-0) = +-015/// - floor(+-0) = +-0
16/// - floor(+-inf) = +-inf16/// - floor(+-inf) = +-inf
17/// - floor(nan) = nan17/// - floor(nan) = nan
18pub fn floor(x: var) @TypeOf(x) {18pub fn floor(x: anytype) @TypeOf(x) {
19 const T = @TypeOf(x);19 const T = @TypeOf(x);
20 return switch (T) {20 return switch (T) {
21 f16 => floor16(x),21 f16 => floor16(x),
22 f32 => floor32(x),22 f32 => floor32(x),
23 f64 => floor64(x),23 f64 => floor64(x),
24 f128 => floor128(x),
24 else => @compileError("floor not implemented for " ++ @typeName(T)),25 else => @compileError("floor not implemented for " ++ @typeName(T)),
25 };26 };
26}27}
...@@ -122,10 +123,38 @@ fn floor64(x: f64) f64 {...@@ -122,10 +123,38 @@ fn floor64(x: f64) f64 {
122 }123 }
123}124}
124125
126fn floor128(x: f128) f128 {
127 const u = @bitCast(u128, x);
128 const e = (u >> 112) & 0x7FFF;
129 var y: f128 = undefined;
130
131 if (e >= 0x3FFF + 112 or x == 0) return x;
132
133 if (u >> 127 != 0) {
134 y = x - math.f128_toint + math.f128_toint - x;
135 } else {
136 y = x + math.f128_toint - math.f128_toint - x;
137 }
138
139 if (e <= 0x3FFF - 1) {
140 math.forceEval(y);
141 if (u >> 127 != 0) {
142 return -1.0;
143 } else {
144 return 0.0;
145 }
146 } else if (y > 0) {
147 return x + y - 1;
148 } else {
149 return x + y;
150 }
151}
152
125test "math.floor" {153test "math.floor" {
126 expect(floor(@as(f16, 1.3)) == floor16(1.3));154 expect(floor(@as(f16, 1.3)) == floor16(1.3));
127 expect(floor(@as(f32, 1.3)) == floor32(1.3));155 expect(floor(@as(f32, 1.3)) == floor32(1.3));
128 expect(floor(@as(f64, 1.3)) == floor64(1.3));156 expect(floor(@as(f64, 1.3)) == floor64(1.3));
157 expect(floor(@as(f128, 1.3)) == floor128(1.3));
129}158}
130159
131test "math.floor16" {160test "math.floor16" {
...@@ -146,6 +175,12 @@ test "math.floor64" {...@@ -146,6 +175,12 @@ test "math.floor64" {
146 expect(floor64(0.2) == 0.0);175 expect(floor64(0.2) == 0.0);
147}176}
148177
178test "math.floor128" {
179 expect(floor128(1.3) == 1.0);
180 expect(floor128(-1.3) == -2.0);
181 expect(floor128(0.2) == 0.0);
182}
183
149test "math.floor16.special" {184test "math.floor16.special" {
150 expect(floor16(0.0) == 0.0);185 expect(floor16(0.0) == 0.0);
151 expect(floor16(-0.0) == -0.0);186 expect(floor16(-0.0) == -0.0);
...@@ -169,3 +204,11 @@ test "math.floor64.special" {...@@ -169,3 +204,11 @@ test "math.floor64.special" {
169 expect(math.isNegativeInf(floor64(-math.inf(f64))));204 expect(math.isNegativeInf(floor64(-math.inf(f64))));
170 expect(math.isNan(floor64(math.nan(f64))));205 expect(math.isNan(floor64(math.nan(f64))));
171}206}
207
208test "math.floor128.special" {
209 expect(floor128(0.0) == 0.0);
210 expect(floor128(-0.0) == -0.0);
211 expect(math.isPositiveInf(floor128(math.inf(f128))));
212 expect(math.isNegativeInf(floor128(-math.inf(f128))));
213 expect(math.isNan(floor128(math.nan(f128))));
214}
lib/std/math/frexp.zig+1-1
...@@ -24,7 +24,7 @@ pub const frexp64_result = frexp_result(f64);...@@ -24,7 +24,7 @@ pub const frexp64_result = frexp_result(f64);
24/// - frexp(+-0) = +-0, 024/// - frexp(+-0) = +-0, 0
25/// - frexp(+-inf) = +-inf, 025/// - frexp(+-inf) = +-inf, 0
26/// - frexp(nan) = nan, undefined26/// - frexp(nan) = nan, undefined
27pub fn frexp(x: var) frexp_result(@TypeOf(x)) {27pub fn frexp(x: anytype) frexp_result(@TypeOf(x)) {
28 const T = @TypeOf(x);28 const T = @TypeOf(x);
29 return switch (T) {29 return switch (T) {
30 f32 => frexp32(x),30 f32 => frexp32(x),
lib/std/math/ilogb.zig+1-1
...@@ -16,7 +16,7 @@ const minInt = std.math.minInt;...@@ -16,7 +16,7 @@ const minInt = std.math.minInt;
16/// - ilogb(+-inf) = maxInt(i32)16/// - ilogb(+-inf) = maxInt(i32)
17/// - ilogb(0) = maxInt(i32)17/// - ilogb(0) = maxInt(i32)
18/// - ilogb(nan) = maxInt(i32)18/// - ilogb(nan) = maxInt(i32)
19pub fn ilogb(x: var) i32 {19pub fn ilogb(x: anytype) i32 {
20 const T = @TypeOf(x);20 const T = @TypeOf(x);
21 return switch (T) {21 return switch (T) {
22 f32 => ilogb32(x),22 f32 => ilogb32(x),
lib/std/math/isfinite.zig+1-1
...@@ -4,7 +4,7 @@ const expect = std.testing.expect;...@@ -4,7 +4,7 @@ const expect = std.testing.expect;
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
55
6/// Returns whether x is a finite value.6/// Returns whether x is a finite value.
7pub fn isFinite(x: var) bool {7pub fn isFinite(x: anytype) bool {
8 const T = @TypeOf(x);8 const T = @TypeOf(x);
9 switch (T) {9 switch (T) {
10 f16 => {10 f16 => {
lib/std/math/isinf.zig+3-3
...@@ -4,7 +4,7 @@ const expect = std.testing.expect;...@@ -4,7 +4,7 @@ const expect = std.testing.expect;
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
55
6/// Returns whether x is an infinity, ignoring sign.6/// Returns whether x is an infinity, ignoring sign.
7pub fn isInf(x: var) bool {7pub fn isInf(x: anytype) bool {
8 const T = @TypeOf(x);8 const T = @TypeOf(x);
9 switch (T) {9 switch (T) {
10 f16 => {10 f16 => {
...@@ -30,7 +30,7 @@ pub fn isInf(x: var) bool {...@@ -30,7 +30,7 @@ pub fn isInf(x: var) bool {
30}30}
3131
32/// Returns whether x is an infinity with a positive sign.32/// Returns whether x is an infinity with a positive sign.
33pub fn isPositiveInf(x: var) bool {33pub fn isPositiveInf(x: anytype) bool {
34 const T = @TypeOf(x);34 const T = @TypeOf(x);
35 switch (T) {35 switch (T) {
36 f16 => {36 f16 => {
...@@ -52,7 +52,7 @@ pub fn isPositiveInf(x: var) bool {...@@ -52,7 +52,7 @@ pub fn isPositiveInf(x: var) bool {
52}52}
5353
54/// Returns whether x is an infinity with a negative sign.54/// Returns whether x is an infinity with a negative sign.
55pub fn isNegativeInf(x: var) bool {55pub fn isNegativeInf(x: anytype) bool {
56 const T = @TypeOf(x);56 const T = @TypeOf(x);
57 switch (T) {57 switch (T) {
58 f16 => {58 f16 => {
lib/std/math/isnan.zig+2-2
...@@ -4,12 +4,12 @@ const expect = std.testing.expect;...@@ -4,12 +4,12 @@ const expect = std.testing.expect;
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
55
6/// Returns whether x is a nan.6/// Returns whether x is a nan.
7pub fn isNan(x: var) bool {7pub fn isNan(x: anytype) bool {
8 return x != x;8 return x != x;
9}9}
1010
11/// Returns whether x is a signalling nan.11/// Returns whether x is a signalling nan.
12pub fn isSignalNan(x: var) bool {12pub fn isSignalNan(x: anytype) bool {
13 // Note: A signalling nan is identical to a standard nan right now but may have a different bit13 // Note: A signalling nan is identical to a standard nan right now but may have a different bit
14 // representation in the future when required.14 // representation in the future when required.
15 return isNan(x);15 return isNan(x);
lib/std/math/isnormal.zig+1-1
...@@ -4,7 +4,7 @@ const expect = std.testing.expect;...@@ -4,7 +4,7 @@ const expect = std.testing.expect;
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
55
6// Returns whether x has a normalized representation (i.e. integer part of mantissa is 1).6// Returns whether x has a normalized representation (i.e. integer part of mantissa is 1).
7pub fn isNormal(x: var) bool {7pub fn isNormal(x: anytype) bool {
8 const T = @TypeOf(x);8 const T = @TypeOf(x);
9 switch (T) {9 switch (T) {
10 f16 => {10 f16 => {
lib/std/math/ln.zig+1-1
...@@ -15,7 +15,7 @@ const expect = std.testing.expect;...@@ -15,7 +15,7 @@ const expect = std.testing.expect;
15/// - ln(0) = -inf15/// - ln(0) = -inf
16/// - ln(x) = nan if x < 016/// - ln(x) = nan if x < 0
17/// - ln(nan) = nan17/// - ln(nan) = nan
18pub fn ln(x: var) @TypeOf(x) {18pub fn ln(x: anytype) @TypeOf(x) {
19 const T = @TypeOf(x);19 const T = @TypeOf(x);
20 switch (@typeInfo(T)) {20 switch (@typeInfo(T)) {
21 .ComptimeFloat => {21 .ComptimeFloat => {
lib/std/math/log10.zig+1-1
...@@ -16,7 +16,7 @@ const maxInt = std.math.maxInt;...@@ -16,7 +16,7 @@ const maxInt = std.math.maxInt;
16/// - log10(0) = -inf16/// - log10(0) = -inf
17/// - log10(x) = nan if x < 017/// - log10(x) = nan if x < 0
18/// - log10(nan) = nan18/// - log10(nan) = nan
19pub fn log10(x: var) @TypeOf(x) {19pub fn log10(x: anytype) @TypeOf(x) {
20 const T = @TypeOf(x);20 const T = @TypeOf(x);
21 switch (@typeInfo(T)) {21 switch (@typeInfo(T)) {
22 .ComptimeFloat => {22 .ComptimeFloat => {
lib/std/math/log1p.zig+1-1
...@@ -17,7 +17,7 @@ const expect = std.testing.expect;...@@ -17,7 +17,7 @@ const expect = std.testing.expect;
17/// - log1p(-1) = -inf17/// - log1p(-1) = -inf
18/// - log1p(x) = nan if x < -118/// - log1p(x) = nan if x < -1
19/// - log1p(nan) = nan19/// - log1p(nan) = nan
20pub fn log1p(x: var) @TypeOf(x) {20pub fn log1p(x: anytype) @TypeOf(x) {
21 const T = @TypeOf(x);21 const T = @TypeOf(x);
22 return switch (T) {22 return switch (T) {
23 f32 => log1p_32(x),23 f32 => log1p_32(x),
lib/std/math/log2.zig+1-1
...@@ -16,7 +16,7 @@ const maxInt = std.math.maxInt;...@@ -16,7 +16,7 @@ const maxInt = std.math.maxInt;
16/// - log2(0) = -inf16/// - log2(0) = -inf
17/// - log2(x) = nan if x < 017/// - log2(x) = nan if x < 0
18/// - log2(nan) = nan18/// - log2(nan) = nan
19pub fn log2(x: var) @TypeOf(x) {19pub fn log2(x: anytype) @TypeOf(x) {
20 const T = @TypeOf(x);20 const T = @TypeOf(x);
21 switch (@typeInfo(T)) {21 switch (@typeInfo(T)) {
22 .ComptimeFloat => {22 .ComptimeFloat => {
lib/std/math/modf.zig+1-1
...@@ -24,7 +24,7 @@ pub const modf64_result = modf_result(f64);...@@ -24,7 +24,7 @@ pub const modf64_result = modf_result(f64);
24/// Special Cases:24/// Special Cases:
25/// - modf(+-inf) = +-inf, nan25/// - modf(+-inf) = +-inf, nan
26/// - modf(nan) = nan, nan26/// - modf(nan) = nan, nan
27pub fn modf(x: var) modf_result(@TypeOf(x)) {27pub fn modf(x: anytype) modf_result(@TypeOf(x)) {
28 const T = @TypeOf(x);28 const T = @TypeOf(x);
29 return switch (T) {29 return switch (T) {
30 f32 => modf32(x),30 f32 => modf32(x),
lib/std/math/round.zig+51-1
...@@ -15,11 +15,12 @@ const math = std.math;...@@ -15,11 +15,12 @@ const math = std.math;
15/// - round(+-0) = +-015/// - round(+-0) = +-0
16/// - round(+-inf) = +-inf16/// - round(+-inf) = +-inf
17/// - round(nan) = nan17/// - round(nan) = nan
18pub fn round(x: var) @TypeOf(x) {18pub fn round(x: anytype) @TypeOf(x) {
19 const T = @TypeOf(x);19 const T = @TypeOf(x);
20 return switch (T) {20 return switch (T) {
21 f32 => round32(x),21 f32 => round32(x),
22 f64 => round64(x),22 f64 => round64(x),
23 f128 => round128(x),
23 else => @compileError("round not implemented for " ++ @typeName(T)),24 else => @compileError("round not implemented for " ++ @typeName(T)),
24 };25 };
25}26}
...@@ -90,9 +91,43 @@ fn round64(x_: f64) f64 {...@@ -90,9 +91,43 @@ fn round64(x_: f64) f64 {
90 }91 }
91}92}
9293
94fn round128(x_: f128) f128 {
95 var x = x_;
96 const u = @bitCast(u128, x);
97 const e = (u >> 112) & 0x7FFF;
98 var y: f128 = undefined;
99
100 if (e >= 0x3FFF + 112) {
101 return x;
102 }
103 if (u >> 127 != 0) {
104 x = -x;
105 }
106 if (e < 0x3FFF - 1) {
107 math.forceEval(x + math.f64_toint);
108 return 0 * @bitCast(f128, u);
109 }
110
111 y = x + math.f128_toint - math.f128_toint - x;
112 if (y > 0.5) {
113 y = y + x - 1;
114 } else if (y <= -0.5) {
115 y = y + x + 1;
116 } else {
117 y = y + x;
118 }
119
120 if (u >> 127 != 0) {
121 return -y;
122 } else {
123 return y;
124 }
125}
126
93test "math.round" {127test "math.round" {
94 expect(round(@as(f32, 1.3)) == round32(1.3));128 expect(round(@as(f32, 1.3)) == round32(1.3));
95 expect(round(@as(f64, 1.3)) == round64(1.3));129 expect(round(@as(f64, 1.3)) == round64(1.3));
130 expect(round(@as(f128, 1.3)) == round128(1.3));
96}131}
97132
98test "math.round32" {133test "math.round32" {
...@@ -109,6 +144,13 @@ test "math.round64" {...@@ -109,6 +144,13 @@ test "math.round64" {
109 expect(round64(1.8) == 2.0);144 expect(round64(1.8) == 2.0);
110}145}
111146
147test "math.round128" {
148 expect(round128(1.3) == 1.0);
149 expect(round128(-1.3) == -1.0);
150 expect(round128(0.2) == 0.0);
151 expect(round128(1.8) == 2.0);
152}
153
112test "math.round32.special" {154test "math.round32.special" {
113 expect(round32(0.0) == 0.0);155 expect(round32(0.0) == 0.0);
114 expect(round32(-0.0) == -0.0);156 expect(round32(-0.0) == -0.0);
...@@ -124,3 +166,11 @@ test "math.round64.special" {...@@ -124,3 +166,11 @@ test "math.round64.special" {
124 expect(math.isNegativeInf(round64(-math.inf(f64))));166 expect(math.isNegativeInf(round64(-math.inf(f64))));
125 expect(math.isNan(round64(math.nan(f64))));167 expect(math.isNan(round64(math.nan(f64))));
126}168}
169
170test "math.round128.special" {
171 expect(round128(0.0) == 0.0);
172 expect(round128(-0.0) == -0.0);
173 expect(math.isPositiveInf(round128(math.inf(f128))));
174 expect(math.isNegativeInf(round128(-math.inf(f128))));
175 expect(math.isNan(round128(math.nan(f128))));
176}
lib/std/math/scalbn.zig+1-1
...@@ -9,7 +9,7 @@ const math = std.math;...@@ -9,7 +9,7 @@ const math = std.math;
9const expect = std.testing.expect;9const expect = std.testing.expect;
1010
11/// Returns x * 2^n.11/// Returns x * 2^n.
12pub fn scalbn(x: var, n: i32) @TypeOf(x) {12pub fn scalbn(x: anytype, n: i32) @TypeOf(x) {
13 const T = @TypeOf(x);13 const T = @TypeOf(x);
14 return switch (T) {14 return switch (T) {
15 f32 => scalbn32(x, n),15 f32 => scalbn32(x, n),
lib/std/math/signbit.zig+1-1
...@@ -3,7 +3,7 @@ const math = std.math;...@@ -3,7 +3,7 @@ const math = std.math;
3const expect = std.testing.expect;3const expect = std.testing.expect;
44
5/// Returns whether x is negative or negative 0.5/// Returns whether x is negative or negative 0.
6pub fn signbit(x: var) bool {6pub fn signbit(x: anytype) bool {
7 const T = @TypeOf(x);7 const T = @TypeOf(x);
8 return switch (T) {8 return switch (T) {
9 f16 => signbit16(x),9 f16 => signbit16(x),
lib/std/math/sin.zig+1-1
...@@ -14,7 +14,7 @@ const expect = std.testing.expect;...@@ -14,7 +14,7 @@ const expect = std.testing.expect;
14/// - sin(+-0) = +-014/// - sin(+-0) = +-0
15/// - sin(+-inf) = nan15/// - sin(+-inf) = nan
16/// - sin(nan) = nan16/// - sin(nan) = nan
17pub fn sin(x: var) @TypeOf(x) {17pub fn sin(x: anytype) @TypeOf(x) {
18 const T = @TypeOf(x);18 const T = @TypeOf(x);
19 return switch (T) {19 return switch (T) {
20 f32 => sin_(T, x),20 f32 => sin_(T, x),
lib/std/math/sinh.zig+1-1
...@@ -17,7 +17,7 @@ const maxInt = std.math.maxInt;...@@ -17,7 +17,7 @@ const maxInt = std.math.maxInt;
17/// - sinh(+-0) = +-017/// - sinh(+-0) = +-0
18/// - sinh(+-inf) = +-inf18/// - sinh(+-inf) = +-inf
19/// - sinh(nan) = nan19/// - sinh(nan) = nan
20pub fn sinh(x: var) @TypeOf(x) {20pub fn sinh(x: anytype) @TypeOf(x) {
21 const T = @TypeOf(x);21 const T = @TypeOf(x);
22 return switch (T) {22 return switch (T) {
23 f32 => sinh32(x),23 f32 => sinh32(x),
lib/std/math/sqrt.zig+1-1
...@@ -13,7 +13,7 @@ const maxInt = std.math.maxInt;...@@ -13,7 +13,7 @@ const maxInt = std.math.maxInt;
13/// - sqrt(x) = nan if x < 013/// - sqrt(x) = nan if x < 0
14/// - sqrt(nan) = nan14/// - sqrt(nan) = nan
15/// TODO Decide if all this logic should be implemented directly in the @sqrt bultin function.15/// TODO Decide if all this logic should be implemented directly in the @sqrt bultin function.
16pub fn sqrt(x: var) Sqrt(@TypeOf(x)) {16pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) {
17 const T = @TypeOf(x);17 const T = @TypeOf(x);
18 switch (@typeInfo(T)) {18 switch (@typeInfo(T)) {
19 .Float, .ComptimeFloat => return @sqrt(x),19 .Float, .ComptimeFloat => return @sqrt(x),
lib/std/math/tan.zig+1-1
...@@ -14,7 +14,7 @@ const expect = std.testing.expect;...@@ -14,7 +14,7 @@ const expect = std.testing.expect;
14/// - tan(+-0) = +-014/// - tan(+-0) = +-0
15/// - tan(+-inf) = nan15/// - tan(+-inf) = nan
16/// - tan(nan) = nan16/// - tan(nan) = nan
17pub fn tan(x: var) @TypeOf(x) {17pub fn tan(x: anytype) @TypeOf(x) {
18 const T = @TypeOf(x);18 const T = @TypeOf(x);
19 return switch (T) {19 return switch (T) {
20 f32 => tan_(f32, x),20 f32 => tan_(f32, x),
lib/std/math/tanh.zig+1-1
...@@ -17,7 +17,7 @@ const maxInt = std.math.maxInt;...@@ -17,7 +17,7 @@ const maxInt = std.math.maxInt;
17/// - sinh(+-0) = +-017/// - sinh(+-0) = +-0
18/// - sinh(+-inf) = +-118/// - sinh(+-inf) = +-1
19/// - sinh(nan) = nan19/// - sinh(nan) = nan
20pub fn tanh(x: var) @TypeOf(x) {20pub fn tanh(x: anytype) @TypeOf(x) {
21 const T = @TypeOf(x);21 const T = @TypeOf(x);
22 return switch (T) {22 return switch (T) {
23 f32 => tanh32(x),23 f32 => tanh32(x),
lib/std/math/trunc.zig+38-1
...@@ -15,11 +15,12 @@ const maxInt = std.math.maxInt;...@@ -15,11 +15,12 @@ const maxInt = std.math.maxInt;
15/// - trunc(+-0) = +-015/// - trunc(+-0) = +-0
16/// - trunc(+-inf) = +-inf16/// - trunc(+-inf) = +-inf
17/// - trunc(nan) = nan17/// - trunc(nan) = nan
18pub fn trunc(x: var) @TypeOf(x) {18pub fn trunc(x: anytype) @TypeOf(x) {
19 const T = @TypeOf(x);19 const T = @TypeOf(x);
20 return switch (T) {20 return switch (T) {
21 f32 => trunc32(x),21 f32 => trunc32(x),
22 f64 => trunc64(x),22 f64 => trunc64(x),
23 f128 => trunc128(x),
23 else => @compileError("trunc not implemented for " ++ @typeName(T)),24 else => @compileError("trunc not implemented for " ++ @typeName(T)),
24 };25 };
25}26}
...@@ -66,9 +67,31 @@ fn trunc64(x: f64) f64 {...@@ -66,9 +67,31 @@ fn trunc64(x: f64) f64 {
66 }67 }
67}68}
6869
70fn trunc128(x: f128) f128 {
71 const u = @bitCast(u128, x);
72 var e = @intCast(i32, ((u >> 112) & 0x7FFF)) - 0x3FFF + 16;
73 var m: u128 = undefined;
74
75 if (e >= 112 + 16) {
76 return x;
77 }
78 if (e < 16) {
79 e = 1;
80 }
81
82 m = @as(u128, maxInt(u128)) >> @intCast(u7, e);
83 if (u & m == 0) {
84 return x;
85 } else {
86 math.forceEval(x + 0x1p120);
87 return @bitCast(f128, u & ~m);
88 }
89}
90
69test "math.trunc" {91test "math.trunc" {
70 expect(trunc(@as(f32, 1.3)) == trunc32(1.3));92 expect(trunc(@as(f32, 1.3)) == trunc32(1.3));
71 expect(trunc(@as(f64, 1.3)) == trunc64(1.3));93 expect(trunc(@as(f64, 1.3)) == trunc64(1.3));
94 expect(trunc(@as(f128, 1.3)) == trunc128(1.3));
72}95}
7396
74test "math.trunc32" {97test "math.trunc32" {
...@@ -83,6 +106,12 @@ test "math.trunc64" {...@@ -83,6 +106,12 @@ test "math.trunc64" {
83 expect(trunc64(0.2) == 0.0);106 expect(trunc64(0.2) == 0.0);
84}107}
85108
109test "math.trunc128" {
110 expect(trunc128(1.3) == 1.0);
111 expect(trunc128(-1.3) == -1.0);
112 expect(trunc128(0.2) == 0.0);
113}
114
86test "math.trunc32.special" {115test "math.trunc32.special" {
87 expect(trunc32(0.0) == 0.0); // 0x3F800000116 expect(trunc32(0.0) == 0.0); // 0x3F800000
88 expect(trunc32(-0.0) == -0.0);117 expect(trunc32(-0.0) == -0.0);
...@@ -98,3 +127,11 @@ test "math.trunc64.special" {...@@ -98,3 +127,11 @@ test "math.trunc64.special" {
98 expect(math.isNegativeInf(trunc64(-math.inf(f64))));127 expect(math.isNegativeInf(trunc64(-math.inf(f64))));
99 expect(math.isNan(trunc64(math.nan(f64))));128 expect(math.isNan(trunc64(math.nan(f64))));
100}129}
130
131test "math.trunc128.special" {
132 expect(trunc128(0.0) == 0.0);
133 expect(trunc128(-0.0) == -0.0);
134 expect(math.isPositiveInf(trunc128(math.inf(f128))));
135 expect(math.isNegativeInf(trunc128(-math.inf(f128))));
136 expect(math.isNan(trunc128(math.nan(f128))));
137}
lib/std/mem.zig+350-86
...@@ -8,6 +8,7 @@ const meta = std.meta;...@@ -8,6 +8,7 @@ const meta = std.meta;
8const trait = meta.trait;8const trait = meta.trait;
9const testing = std.testing;9const testing = std.testing;
1010
11// https://github.com/ziglang/zig/issues/2564
11pub const page_size = switch (builtin.arch) {12pub const page_size = switch (builtin.arch) {
12 .wasm32, .wasm64 => 64 * 1024,13 .wasm32, .wasm64 => 64 * 1024,
13 else => 4 * 1024,14 else => 4 * 1024,
...@@ -16,6 +17,52 @@ pub const page_size = switch (builtin.arch) {...@@ -16,6 +17,52 @@ pub const page_size = switch (builtin.arch) {
16pub const Allocator = struct {17pub const Allocator = struct {
17 pub const Error = error{OutOfMemory};18 pub const Error = error{OutOfMemory};
1819
20 /// Attempt to allocate at least `len` bytes aligned to `ptr_align`.
21 ///
22 /// If `len_align` is `0`, then the length returned MUST be exactly `len` bytes,
23 /// otherwise, the length must be aligned to `len_align`.
24 ///
25 /// `len` must be greater than or equal to `len_align` and must be aligned by `len_align`.
26 allocFn: fn (self: *Allocator, len: usize, ptr_align: u29, len_align: u29) Error![]u8,
27
28 /// Attempt to expand or shrink memory in place. `buf.len` must equal the most recent
29 /// length returned by `allocFn` or `resizeFn`.
30 ///
31 /// Passing a `new_len` of 0 frees and invalidates the buffer such that it can no
32 /// longer be passed to `resizeFn`.
33 ///
34 /// error.OutOfMemory can only be returned if `new_len` is greater than `buf.len`.
35 /// If `buf` cannot be expanded to accomodate `new_len`, then the allocation MUST be
36 /// unmodified and error.OutOfMemory MUST be returned.
37 ///
38 /// If `len_align` is `0`, then the length returned MUST be exactly `len` bytes,
39 /// otherwise, the length must be aligned to `len_align`.
40 ///
41 /// `new_len` must be greater than or equal to `len_align` and must be aligned by `len_align`.
42 resizeFn: fn (self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Error!usize,
43
44 pub fn callAllocFn(self: *Allocator, new_len: usize, alignment: u29, len_align: u29) Error![]u8 {
45 return self.allocFn(self, new_len, alignment, len_align);
46 }
47
48 pub fn callResizeFn(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Error!usize {
49 return self.resizeFn(self, buf, new_len, len_align);
50 }
51
52 /// Set to resizeFn if in-place resize is not supported.
53 pub fn noResize(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) Error!usize {
54 if (new_len > buf.len)
55 return error.OutOfMemory;
56 return new_len;
57 }
58
59 /// Call `resizeFn`, but caller guarantees that `new_len` <= `buf.len` meaning
60 /// error.OutOfMemory should be impossible.
61 pub fn shrinkBytes(self: *Allocator, buf: []u8, new_len: usize, len_align: u29) usize {
62 assert(new_len <= buf.len);
63 return self.callResizeFn(buf, new_len, len_align) catch unreachable;
64 }
65
19 /// Realloc is used to modify the size or alignment of an existing allocation,66 /// Realloc is used to modify the size or alignment of an existing allocation,
20 /// as well as to provide the allocator with an opportunity to move an allocation67 /// as well as to provide the allocator with an opportunity to move an allocation
21 /// to a better location.68 /// to a better location.
...@@ -24,7 +71,7 @@ pub const Allocator = struct {...@@ -24,7 +71,7 @@ pub const Allocator = struct {
24 /// When the size/alignment is less than or equal to the previous allocation,71 /// When the size/alignment is less than or equal to the previous allocation,
25 /// this function returns `error.OutOfMemory` when the allocator decides the client72 /// this function returns `error.OutOfMemory` when the allocator decides the client
26 /// would be better off keeping the extra alignment/size. Clients will call73 /// would be better off keeping the extra alignment/size. Clients will call
27 /// `shrinkFn` when they require the allocator to track a new alignment/size,74 /// `callResizeFn` when they require the allocator to track a new alignment/size,
28 /// and so this function should only return success when the allocator considers75 /// and so this function should only return success when the allocator considers
29 /// the reallocation desirable from the allocator's perspective.76 /// the reallocation desirable from the allocator's perspective.
30 /// As an example, `std.ArrayList` tracks a "capacity", and therefore can handle77 /// As an example, `std.ArrayList` tracks a "capacity", and therefore can handle
...@@ -37,16 +84,15 @@ pub const Allocator = struct {...@@ -37,16 +84,15 @@ pub const Allocator = struct {
37 /// as `old_mem` was when `reallocFn` is called. The bytes of84 /// as `old_mem` was when `reallocFn` is called. The bytes of
38 /// `return_value[old_mem.len..]` have undefined values.85 /// `return_value[old_mem.len..]` have undefined values.
39 /// The returned slice must have its pointer aligned at least to `new_alignment` bytes.86 /// The returned slice must have its pointer aligned at least to `new_alignment` bytes.
40 reallocFn: fn (87 fn reallocBytes(
41 self: *Allocator,88 self: *Allocator,
42 /// Guaranteed to be the same as what was returned from most recent call to89 /// Guaranteed to be the same as what was returned from most recent call to
43 /// `reallocFn` or `shrinkFn`.90 /// `allocFn` or `resizeFn`.
44 /// If `old_mem.len == 0` then this is a new allocation and `new_byte_count`91 /// If `old_mem.len == 0` then this is a new allocation and `new_byte_count`
45 /// is guaranteed to be >= 1.92 /// is guaranteed to be >= 1.
46 old_mem: []u8,93 old_mem: []u8,
47 /// If `old_mem.len == 0` then this is `undefined`, otherwise:94 /// If `old_mem.len == 0` then this is `undefined`, otherwise:
48 /// Guaranteed to be the same as what was returned from most recent call to95 /// Guaranteed to be the same as what was passed to `allocFn`.
49 /// `reallocFn` or `shrinkFn`.
50 /// Guaranteed to be >= 1.96 /// Guaranteed to be >= 1.
51 /// Guaranteed to be a power of 2.97 /// Guaranteed to be a power of 2.
52 old_alignment: u29,98 old_alignment: u29,
...@@ -57,23 +103,49 @@ pub const Allocator = struct {...@@ -57,23 +103,49 @@ pub const Allocator = struct {
57 /// Guaranteed to be a power of 2.103 /// Guaranteed to be a power of 2.
58 /// Returned slice's pointer must have this alignment.104 /// Returned slice's pointer must have this alignment.
59 new_alignment: u29,105 new_alignment: u29,
60 ) Error![]u8,106 /// 0 indicates the length of the slice returned MUST match `new_byte_count` exactly
107 /// non-zero means the length of the returned slice must be aligned by `len_align`
108 /// `new_len` must be aligned by `len_align`
109 len_align: u29,
110 ) Error![]u8 {
111 if (old_mem.len == 0) {
112 const new_mem = try self.callAllocFn(new_byte_count, new_alignment, len_align);
113 @memset(new_mem.ptr, undefined, new_byte_count);
114 return new_mem;
115 }
61116
62 /// This function deallocates memory. It must succeed.117 if (isAligned(@ptrToInt(old_mem.ptr), new_alignment)) {
63 shrinkFn: fn (118 if (new_byte_count <= old_mem.len) {
64 self: *Allocator,119 const shrunk_len = self.shrinkBytes(old_mem, new_byte_count, len_align);
65 /// Guaranteed to be the same as what was returned from most recent call to120 return old_mem.ptr[0..shrunk_len];
66 /// `reallocFn` or `shrinkFn`.121 }
67 old_mem: []u8,122 if (self.callResizeFn(old_mem, new_byte_count, len_align)) |resized_len| {
68 /// Guaranteed to be the same as what was returned from most recent call to123 assert(resized_len >= new_byte_count);
69 /// `reallocFn` or `shrinkFn`.124 @memset(old_mem.ptr + new_byte_count, undefined, resized_len - new_byte_count);
70 old_alignment: u29,125 return old_mem.ptr[0..resized_len];
71 /// Guaranteed to be less than or equal to `old_mem.len`.126 } else |_| {}
72 new_byte_count: usize,127 }
73 /// If `new_byte_count == 0` then this is `undefined`, otherwise:128 if (new_byte_count <= old_mem.len and new_alignment <= old_alignment) {
74 /// Guaranteed to be less than or equal to `old_alignment`.129 return error.OutOfMemory;
75 new_alignment: u29,130 }
76 ) []u8,131 return self.moveBytes(old_mem, new_byte_count, new_alignment, len_align);
132 }
133
134 /// Move the given memory to a new location in the given allocator to accomodate a new
135 /// size and alignment.
136 fn moveBytes(self: *Allocator, old_mem: []u8, new_len: usize, new_alignment: u29, len_align: u29) Error![]u8 {
137 assert(old_mem.len > 0);
138 assert(new_len > 0);
139 const new_mem = try self.callAllocFn(new_len, new_alignment, len_align);
140 @memcpy(new_mem.ptr, old_mem.ptr, std.math.min(new_len, old_mem.len));
141 // DISABLED TO AVOID BUGS IN TRANSLATE C
142 // use './zig build test-translate-c' to reproduce, some of the symbols in the
143 // generated C code will be a sequence of 0xaa (the undefined value), meaning
144 // it is printing data that has been freed
145 //@memset(old_mem.ptr, undefined, old_mem.len);
146 _ = self.shrinkBytes(old_mem, 0, 0);
147 return new_mem;
148 }
77149
78 /// Returns a pointer to undefined memory.150 /// Returns a pointer to undefined memory.
79 /// Call `destroy` with the result to free the memory.151 /// Call `destroy` with the result to free the memory.
...@@ -85,12 +157,11 @@ pub const Allocator = struct {...@@ -85,12 +157,11 @@ pub const Allocator = struct {
85157
86 /// `ptr` should be the return value of `create`, or otherwise158 /// `ptr` should be the return value of `create`, or otherwise
87 /// have the same address and alignment property.159 /// have the same address and alignment property.
88 pub fn destroy(self: *Allocator, ptr: var) void {160 pub fn destroy(self: *Allocator, ptr: anytype) void {
89 const T = @TypeOf(ptr).Child;161 const T = @TypeOf(ptr).Child;
90 if (@sizeOf(T) == 0) return;162 if (@sizeOf(T) == 0) return;
91 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));163 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
92 const shrink_result = self.shrinkFn(self, non_const_ptr[0..@sizeOf(T)], @alignOf(T), 0, 1);164 _ = self.shrinkBytes(non_const_ptr[0..@sizeOf(T)], 0, 0);
93 assert(shrink_result.len == 0);
94 }165 }
95166
96 /// Allocates an array of `n` items of type `T` and sets all the167 /// Allocates an array of `n` items of type `T` and sets all the
...@@ -144,15 +215,28 @@ pub const Allocator = struct {...@@ -144,15 +215,28 @@ pub const Allocator = struct {
144 return self.allocWithOptions(Elem, n, null, sentinel);215 return self.allocWithOptions(Elem, n, null, sentinel);
145 }216 }
146217
218 /// Deprecated: use `allocAdvanced`
147 pub fn alignedAlloc(219 pub fn alignedAlloc(
148 self: *Allocator,220 self: *Allocator,
149 comptime T: type,221 comptime T: type,
150 /// null means naturally aligned222 /// null means naturally aligned
151 comptime alignment: ?u29,223 comptime alignment: ?u29,
152 n: usize,224 n: usize,
225 ) Error![]align(alignment orelse @alignOf(T)) T {
226 return self.allocAdvanced(T, alignment, n, .exact);
227 }
228
229 const Exact = enum { exact, at_least };
230 pub fn allocAdvanced(
231 self: *Allocator,
232 comptime T: type,
233 /// null means naturally aligned
234 comptime alignment: ?u29,
235 n: usize,
236 exact: Exact,
153 ) Error![]align(alignment orelse @alignOf(T)) T {237 ) Error![]align(alignment orelse @alignOf(T)) T {
154 const a = if (alignment) |a| blk: {238 const a = if (alignment) |a| blk: {
155 if (a == @alignOf(T)) return alignedAlloc(self, T, null, n);239 if (a == @alignOf(T)) return allocAdvanced(self, T, null, n, exact);
156 break :blk a;240 break :blk a;
157 } else @alignOf(T);241 } else @alignOf(T);
158242
...@@ -161,15 +245,19 @@ pub const Allocator = struct {...@@ -161,15 +245,19 @@ pub const Allocator = struct {
161 }245 }
162246
163 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;247 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
164 const byte_slice = try self.reallocFn(self, &[0]u8{}, undefined, byte_count, a);248 // TODO The `if (alignment == null)` blocks are workarounds for zig not being able to
165 assert(byte_slice.len == byte_count);249 // access certain type information about T without creating a circular dependency in async
250 // functions that heap-allocate their own frame with @Frame(func).
251 const sizeOfT = if (alignment == null) @intCast(u29, @divExact(byte_count, n)) else @sizeOf(T);
252 const byte_slice = try self.callAllocFn(byte_count, a, if (exact == .exact) @as(u29, 0) else sizeOfT);
253 switch (exact) {
254 .exact => assert(byte_slice.len == byte_count),
255 .at_least => assert(byte_slice.len >= byte_count),
256 }
166 @memset(byte_slice.ptr, undefined, byte_slice.len);257 @memset(byte_slice.ptr, undefined, byte_slice.len);
167 if (alignment == null) {258 if (alignment == null) {
168 // TODO This is a workaround for zig not being able to successfully do259 // This if block is a workaround (see comment above)
169 // @bytesToSlice(T, @alignCast(a, byte_slice)) without resolving alignment of T,260 return @intToPtr([*]T, @ptrToInt(byte_slice.ptr))[0..@divExact(byte_slice.len, @sizeOf(T))];
170 // which causes a circular dependency in async functions which try to heap-allocate
171 // their own frame with @Frame(func).
172 return @intToPtr([*]T, @ptrToInt(byte_slice.ptr))[0..n];
173 } else {261 } else {
174 return mem.bytesAsSlice(T, @alignCast(a, byte_slice));262 return mem.bytesAsSlice(T, @alignCast(a, byte_slice));
175 }263 }
...@@ -185,27 +273,46 @@ pub const Allocator = struct {...@@ -185,27 +273,46 @@ pub const Allocator = struct {
185 /// in `std.ArrayList.shrink`.273 /// in `std.ArrayList.shrink`.
186 /// If you need guaranteed success, call `shrink`.274 /// If you need guaranteed success, call `shrink`.
187 /// If `new_n` is 0, this is the same as `free` and it always succeeds.275 /// If `new_n` is 0, this is the same as `free` and it always succeeds.
188 pub fn realloc(self: *Allocator, old_mem: var, new_n: usize) t: {276 pub fn realloc(self: *Allocator, old_mem: anytype, new_n: usize) t: {
277 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
278 break :t Error![]align(Slice.alignment) Slice.child;
279 } {
280 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
281 return self.reallocAdvanced(old_mem, old_alignment, new_n, .exact);
282 }
283
284 pub fn reallocAtLeast(self: *Allocator, old_mem: anytype, new_n: usize) t: {
189 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;285 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
190 break :t Error![]align(Slice.alignment) Slice.child;286 break :t Error![]align(Slice.alignment) Slice.child;
191 } {287 } {
192 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;288 const old_alignment = @typeInfo(@TypeOf(old_mem)).Pointer.alignment;
193 return self.alignedRealloc(old_mem, old_alignment, new_n);289 return self.reallocAdvanced(old_mem, old_alignment, new_n, .at_least);
290 }
291
292 // Deprecated: use `reallocAdvanced`
293 pub fn alignedRealloc(
294 self: *Allocator,
295 old_mem: anytype,
296 comptime new_alignment: u29,
297 new_n: usize,
298 ) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
299 return self.reallocAdvanced(old_mem, new_alignment, new_n, .exact);
194 }300 }
195301
196 /// This is the same as `realloc`, except caller may additionally request302 /// This is the same as `realloc`, except caller may additionally request
197 /// a new alignment, which can be larger, smaller, or the same as the old303 /// a new alignment, which can be larger, smaller, or the same as the old
198 /// allocation.304 /// allocation.
199 pub fn alignedRealloc(305 pub fn reallocAdvanced(
200 self: *Allocator,306 self: *Allocator,
201 old_mem: var,307 old_mem: anytype,
202 comptime new_alignment: u29,308 comptime new_alignment: u29,
203 new_n: usize,309 new_n: usize,
310 exact: Exact,
204 ) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {311 ) Error![]align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
205 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;312 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
206 const T = Slice.child;313 const T = Slice.child;
207 if (old_mem.len == 0) {314 if (old_mem.len == 0) {
208 return self.alignedAlloc(T, new_alignment, new_n);315 return self.allocAdvanced(T, new_alignment, new_n, exact);
209 }316 }
210 if (new_n == 0) {317 if (new_n == 0) {
211 self.free(old_mem);318 self.free(old_mem);
...@@ -215,12 +322,8 @@ pub const Allocator = struct {...@@ -215,12 +322,8 @@ pub const Allocator = struct {
215 const old_byte_slice = mem.sliceAsBytes(old_mem);322 const old_byte_slice = mem.sliceAsBytes(old_mem);
216 const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory;323 const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return Error.OutOfMemory;
217 // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure324 // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure
218 const byte_slice = try self.reallocFn(self, old_byte_slice, Slice.alignment, byte_count, new_alignment);325 const new_byte_slice = try self.reallocBytes(old_byte_slice, Slice.alignment, byte_count, new_alignment, if (exact == .exact) @as(u29, 0) else @sizeOf(T));
219 assert(byte_slice.len == byte_count);326 return mem.bytesAsSlice(T, @alignCast(new_alignment, new_byte_slice));
220 if (new_n > old_mem.len) {
221 @memset(byte_slice.ptr + old_byte_slice.len, undefined, byte_slice.len - old_byte_slice.len);
222 }
223 return mem.bytesAsSlice(T, @alignCast(new_alignment, byte_slice));
224 }327 }
225328
226 /// Prefer calling realloc to shrink if you can tolerate failure, such as329 /// Prefer calling realloc to shrink if you can tolerate failure, such as
...@@ -228,7 +331,7 @@ pub const Allocator = struct {...@@ -228,7 +331,7 @@ pub const Allocator = struct {
228 /// Shrink always succeeds, and `new_n` must be <= `old_mem.len`.331 /// Shrink always succeeds, and `new_n` must be <= `old_mem.len`.
229 /// Returned slice has same alignment as old_mem.332 /// Returned slice has same alignment as old_mem.
230 /// Shrinking to 0 is the same as calling `free`.333 /// Shrinking to 0 is the same as calling `free`.
231 pub fn shrink(self: *Allocator, old_mem: var, new_n: usize) t: {334 pub fn shrink(self: *Allocator, old_mem: anytype, new_n: usize) t: {
232 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;335 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
233 break :t []align(Slice.alignment) Slice.child;336 break :t []align(Slice.alignment) Slice.child;
234 } {337 } {
...@@ -241,19 +344,16 @@ pub const Allocator = struct {...@@ -241,19 +344,16 @@ pub const Allocator = struct {
241 /// allocation.344 /// allocation.
242 pub fn alignedShrink(345 pub fn alignedShrink(
243 self: *Allocator,346 self: *Allocator,
244 old_mem: var,347 old_mem: anytype,
245 comptime new_alignment: u29,348 comptime new_alignment: u29,
246 new_n: usize,349 new_n: usize,
247 ) []align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {350 ) []align(new_alignment) @typeInfo(@TypeOf(old_mem)).Pointer.child {
248 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;351 const Slice = @typeInfo(@TypeOf(old_mem)).Pointer;
249 const T = Slice.child;352 const T = Slice.child;
250353
251 if (new_n == 0) {354 if (new_n == old_mem.len)
252 self.free(old_mem);355 return old_mem;
253 return old_mem[0..0];356 assert(new_n < old_mem.len);
254 }
255
256 assert(new_n <= old_mem.len);
257 assert(new_alignment <= Slice.alignment);357 assert(new_alignment <= Slice.alignment);
258358
259 // Here we skip the overflow checking on the multiplication because359 // Here we skip the overflow checking on the multiplication because
...@@ -262,22 +362,20 @@ pub const Allocator = struct {...@@ -262,22 +362,20 @@ pub const Allocator = struct {
262362
263 const old_byte_slice = mem.sliceAsBytes(old_mem);363 const old_byte_slice = mem.sliceAsBytes(old_mem);
264 @memset(old_byte_slice.ptr + byte_count, undefined, old_byte_slice.len - byte_count);364 @memset(old_byte_slice.ptr + byte_count, undefined, old_byte_slice.len - byte_count);
265 const byte_slice = self.shrinkFn(self, old_byte_slice, Slice.alignment, byte_count, new_alignment);365 _ = self.shrinkBytes(old_byte_slice, byte_count, 0);
266 assert(byte_slice.len == byte_count);366 return old_mem[0..new_n];
267 return mem.bytesAsSlice(T, @alignCast(new_alignment, byte_slice));
268 }367 }
269368
270 /// Free an array allocated with `alloc`. To free a single item,369 /// Free an array allocated with `alloc`. To free a single item,
271 /// see `destroy`.370 /// see `destroy`.
272 pub fn free(self: *Allocator, memory: var) void {371 pub fn free(self: *Allocator, memory: anytype) void {
273 const Slice = @typeInfo(@TypeOf(memory)).Pointer;372 const Slice = @typeInfo(@TypeOf(memory)).Pointer;
274 const bytes = mem.sliceAsBytes(memory);373 const bytes = mem.sliceAsBytes(memory);
275 const bytes_len = bytes.len + if (Slice.sentinel != null) @sizeOf(Slice.child) else 0;374 const bytes_len = bytes.len + if (Slice.sentinel != null) @sizeOf(Slice.child) else 0;
276 if (bytes_len == 0) return;375 if (bytes_len == 0) return;
277 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));376 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));
278 @memset(non_const_ptr, undefined, bytes_len);377 @memset(non_const_ptr, undefined, bytes_len);
279 const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes_len], Slice.alignment, 0, 1);378 _ = self.shrinkBytes(non_const_ptr[0..bytes_len], 0, 0);
280 assert(shrink_result.len == 0);
281 }379 }
282380
283 /// Copies `m` to newly allocated memory. Caller owns the memory.381 /// Copies `m` to newly allocated memory. Caller owns the memory.
...@@ -296,16 +394,96 @@ pub const Allocator = struct {...@@ -296,16 +394,96 @@ pub const Allocator = struct {
296 }394 }
297};395};
298396
397/// Detects and asserts if the std.mem.Allocator interface is violated by the caller
398/// or the allocator.
399pub fn ValidationAllocator(comptime T: type) type {
400 return struct {
401 const Self = @This();
402 allocator: Allocator,
403 underlying_allocator: T,
404 pub fn init(allocator: T) @This() {
405 return .{
406 .allocator = .{
407 .allocFn = alloc,
408 .resizeFn = resize,
409 },
410 .underlying_allocator = allocator,
411 };
412 }
413 fn getUnderlyingAllocatorPtr(self: *@This()) *Allocator {
414 if (T == *Allocator) return self.underlying_allocator;
415 if (*T == *Allocator) return &self.underlying_allocator;
416 return &self.underlying_allocator.allocator;
417 }
418 pub fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) Allocator.Error![]u8 {
419 assert(n > 0);
420 assert(mem.isValidAlign(ptr_align));
421 if (len_align != 0) {
422 assert(mem.isAlignedAnyAlign(n, len_align));
423 assert(n >= len_align);
424 }
425
426 const self = @fieldParentPtr(@This(), "allocator", allocator);
427 const result = try self.getUnderlyingAllocatorPtr().callAllocFn(n, ptr_align, len_align);
428 assert(mem.isAligned(@ptrToInt(result.ptr), ptr_align));
429 if (len_align == 0) {
430 assert(result.len == n);
431 } else {
432 assert(result.len >= n);
433 assert(mem.isAlignedAnyAlign(result.len, len_align));
434 }
435 return result;
436 }
437 pub fn resize(allocator: *Allocator, buf: []u8, new_len: usize, len_align: u29) Allocator.Error!usize {
438 assert(buf.len > 0);
439 if (len_align != 0) {
440 assert(mem.isAlignedAnyAlign(new_len, len_align));
441 assert(new_len >= len_align);
442 }
443 const self = @fieldParentPtr(@This(), "allocator", allocator);
444 const result = try self.getUnderlyingAllocatorPtr().callResizeFn(buf, new_len, len_align);
445 if (len_align == 0) {
446 assert(result == new_len);
447 } else {
448 assert(result >= new_len);
449 assert(mem.isAlignedAnyAlign(result, len_align));
450 }
451 return result;
452 }
453 pub usingnamespace if (T == *Allocator or !@hasDecl(T, "reset")) struct {} else struct {
454 pub fn reset(self: *Self) void {
455 self.underlying_allocator.reset();
456 }
457 };
458 };
459}
460
461pub fn validationWrap(allocator: anytype) ValidationAllocator(@TypeOf(allocator)) {
462 return ValidationAllocator(@TypeOf(allocator)).init(allocator);
463}
464
465/// An allocator helper function. Adjusts an allocation length satisfy `len_align`.
466/// `full_len` should be the full capacity of the allocation which may be greater
467/// than the `len` that was requsted. This function should only be used by allocators
468/// that are unaffected by `len_align`.
469pub fn alignAllocLen(full_len: usize, alloc_len: usize, len_align: u29) usize {
470 assert(alloc_len > 0);
471 assert(alloc_len >= len_align);
472 assert(full_len >= alloc_len);
473 if (len_align == 0)
474 return alloc_len;
475 const adjusted = alignBackwardAnyAlign(full_len, len_align);
476 assert(adjusted >= alloc_len);
477 return adjusted;
478}
479
299var failAllocator = Allocator{480var failAllocator = Allocator{
300 .reallocFn = failAllocatorRealloc,481 .allocFn = failAllocatorAlloc,
301 .shrinkFn = failAllocatorShrink,482 .resizeFn = Allocator.noResize,
302};483};
303fn failAllocatorRealloc(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {484fn failAllocatorAlloc(self: *Allocator, n: usize, alignment: u29, len_align: u29) Allocator.Error![]u8 {
304 return error.OutOfMemory;485 return error.OutOfMemory;
305}486}
306fn failAllocatorShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
307 @panic("failAllocatorShrink should never be called because it cannot allocate");
308}
309487
310test "mem.Allocator basics" {488test "mem.Allocator basics" {
311 testing.expectError(error.OutOfMemory, failAllocator.alloc(u8, 1));489 testing.expectError(error.OutOfMemory, failAllocator.alloc(u8, 1));
...@@ -341,6 +519,7 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {...@@ -341,6 +519,7 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
341 }519 }
342}520}
343521
522/// Sets all elements of `dest` to `value`.
344pub fn set(comptime T: type, dest: []T, value: T) void {523pub fn set(comptime T: type, dest: []T, value: T) void {
345 for (dest) |*d|524 for (dest) |*d|
346 d.* = value;525 d.* = value;
...@@ -373,7 +552,7 @@ pub fn zeroes(comptime T: type) T {...@@ -373,7 +552,7 @@ pub fn zeroes(comptime T: type) T {
373 if (@sizeOf(T) == 0) return T{};552 if (@sizeOf(T) == 0) return T{};
374 if (comptime meta.containerLayout(T) == .Extern) {553 if (comptime meta.containerLayout(T) == .Extern) {
375 var item: T = undefined;554 var item: T = undefined;
376 @memset(@ptrCast([*]u8, &item), 0, @sizeOf(T));555 set(u8, asBytes(&item), 0);
377 return item;556 return item;
378 } else {557 } else {
379 var structure: T = undefined;558 var structure: T = undefined;
...@@ -498,6 +677,8 @@ test "mem.zeroes" {...@@ -498,6 +677,8 @@ test "mem.zeroes" {
498 }677 }
499}678}
500679
680/// Sets a slice to zeroes.
681/// Prevents the store from being optimized out.
501pub fn secureZero(comptime T: type, s: []T) void {682pub fn secureZero(comptime T: type, s: []T) void {
502 // NOTE: We do not use a volatile slice cast here since LLVM cannot683 // NOTE: We do not use a volatile slice cast here since LLVM cannot
503 // see that it can be replaced by a memset.684 // see that it can be replaced by a memset.
...@@ -519,7 +700,7 @@ test "mem.secureZero" {...@@ -519,7 +700,7 @@ test "mem.secureZero" {
519/// Initializes all fields of the struct with their default value, or zero values if no default value is present.700/// Initializes all fields of the struct with their default value, or zero values if no default value is present.
520/// If the field is present in the provided initial values, it will have that value instead.701/// If the field is present in the provided initial values, it will have that value instead.
521/// Structs are initialized recursively.702/// Structs are initialized recursively.
522pub fn zeroInit(comptime T: type, init: var) T {703pub fn zeroInit(comptime T: type, init: anytype) T {
523 comptime const Init = @TypeOf(init);704 comptime const Init = @TypeOf(init);
524705
525 switch (@typeInfo(T)) {706 switch (@typeInfo(T)) {
...@@ -528,6 +709,13 @@ pub fn zeroInit(comptime T: type, init: var) T {...@@ -528,6 +709,13 @@ pub fn zeroInit(comptime T: type, init: var) T {
528 .Struct => |init_info| {709 .Struct => |init_info| {
529 var value = std.mem.zeroes(T);710 var value = std.mem.zeroes(T);
530711
712 if (init_info.is_tuple) {
713 inline for (init_info.fields) |field, i| {
714 @field(value, struct_info.fields[i].name) = @field(init, field.name);
715 }
716 return value;
717 }
718
531 inline for (init_info.fields) |field| {719 inline for (init_info.fields) |field| {
532 if (!@hasField(T, field.name)) {720 if (!@hasField(T, field.name)) {
533 @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));721 @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));
...@@ -544,8 +732,8 @@ pub fn zeroInit(comptime T: type, init: var) T {...@@ -544,8 +732,8 @@ pub fn zeroInit(comptime T: type, init: var) T {
544 @field(value, field.name) = @field(init, field.name);732 @field(value, field.name) = @field(init, field.name);
545 },733 },
546 }734 }
547 } else if (field.default_value != null) {735 } else if (field.default_value) |default_value| {
548 @field(value, field.name) = field.default_value;736 @field(value, field.name) = default_value;
549 }737 }
550 }738 }
551739
...@@ -572,24 +760,40 @@ test "zeroInit" {...@@ -572,24 +760,40 @@ test "zeroInit" {
572 b: ?bool,760 b: ?bool,
573 c: I,761 c: I,
574 e: [3]u8,762 e: [3]u8,
575 f: i64,763 f: i64 = -1,
576 };764 };
577765
578 const s = zeroInit(S, .{766 const s = zeroInit(S, .{
579 .a = 42,767 .a = 42,
580 });768 });
581769
582 testing.expectEqual(s, S{770 testing.expectEqual(S{
583 .a = 42,771 .a = 42,
584 .b = null,772 .b = null,
585 .c = .{773 .c = .{
586 .d = 0,774 .d = 0,
587 },775 },
588 .e = [3]u8{ 0, 0, 0 },776 .e = [3]u8{ 0, 0, 0 },
589 .f = 0,777 .f = -1,
590 });778 }, s);
779
780 const Color = struct {
781 r: u8,
782 g: u8,
783 b: u8,
784 a: u8,
785 };
786
787 const c = zeroInit(Color, .{ 255, 255 });
788 testing.expectEqual(Color{
789 .r = 255,
790 .g = 255,
791 .b = 0,
792 .a = 0,
793 }, c);
591}794}
592795
796/// Compares two slices of numbers lexicographically. O(n).
593pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order {797pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order {
594 const n = math.min(lhs.len, rhs.len);798 const n = math.min(lhs.len, rhs.len);
595 var i: usize = 0;799 var i: usize = 0;
...@@ -719,7 +923,7 @@ test "Span" {...@@ -719,7 +923,7 @@ test "Span" {
719///923///
720/// When there is both a sentinel and an array length or slice length, the924/// When there is both a sentinel and an array length or slice length, the
721/// length value is used instead of the sentinel.925/// length value is used instead of the sentinel.
722pub fn span(ptr: var) Span(@TypeOf(ptr)) {926pub fn span(ptr: anytype) Span(@TypeOf(ptr)) {
723 if (@typeInfo(@TypeOf(ptr)) == .Optional) {927 if (@typeInfo(@TypeOf(ptr)) == .Optional) {
724 if (ptr) |non_null| {928 if (ptr) |non_null| {
725 return span(non_null);929 return span(non_null);
...@@ -747,7 +951,7 @@ test "span" {...@@ -747,7 +951,7 @@ test "span" {
747/// Same as `span`, except when there is both a sentinel and an array951/// Same as `span`, except when there is both a sentinel and an array
748/// length or slice length, scans the memory for the sentinel value952/// length or slice length, scans the memory for the sentinel value
749/// rather than using the length.953/// rather than using the length.
750pub fn spanZ(ptr: var) Span(@TypeOf(ptr)) {954pub fn spanZ(ptr: anytype) Span(@TypeOf(ptr)) {
751 if (@typeInfo(@TypeOf(ptr)) == .Optional) {955 if (@typeInfo(@TypeOf(ptr)) == .Optional) {
752 if (ptr) |non_null| {956 if (ptr) |non_null| {
753 return spanZ(non_null);957 return spanZ(non_null);
...@@ -776,7 +980,7 @@ test "spanZ" {...@@ -776,7 +980,7 @@ test "spanZ" {
776/// or a slice, and returns the length.980/// or a slice, and returns the length.
777/// In the case of a sentinel-terminated array, it uses the array length.981/// In the case of a sentinel-terminated array, it uses the array length.
778/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.982/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
779pub fn len(value: var) usize {983pub fn len(value: anytype) usize {
780 return switch (@typeInfo(@TypeOf(value))) {984 return switch (@typeInfo(@TypeOf(value))) {
781 .Array => |info| info.len,985 .Array => |info| info.len,
782 .Vector => |info| info.len,986 .Vector => |info| info.len,
...@@ -824,7 +1028,7 @@ test "len" {...@@ -824,7 +1028,7 @@ test "len" {
824/// In the case of a sentinel-terminated array, it scans the array1028/// In the case of a sentinel-terminated array, it scans the array
825/// for a sentinel and uses that for the length, rather than using the array length.1029/// for a sentinel and uses that for the length, rather than using the array length.
826/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.1030/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
827pub fn lenZ(ptr: var) usize {1031pub fn lenZ(ptr: anytype) usize {
828 return switch (@typeInfo(@TypeOf(ptr))) {1032 return switch (@typeInfo(@TypeOf(ptr))) {
829 .Array => |info| if (info.sentinel) |sentinel|1033 .Array => |info| if (info.sentinel) |sentinel|
830 indexOfSentinel(info.child, sentinel, &ptr)1034 indexOfSentinel(info.child, sentinel, &ptr)
...@@ -1492,12 +1696,23 @@ pub const SplitIterator = struct {...@@ -1492,12 +1696,23 @@ pub const SplitIterator = struct {
1492/// Naively combines a series of slices with a separator.1696/// Naively combines a series of slices with a separator.
1493/// Allocates memory for the result, which must be freed by the caller.1697/// Allocates memory for the result, which must be freed by the caller.
1494pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 {1698pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 {
1699 return joinMaybeZ(allocator, separator, slices, false);
1700}
1701
1702/// Naively combines a series of slices with a separator and null terminator.
1703/// Allocates memory for the result, which must be freed by the caller.
1704pub fn joinZ(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![:0]u8 {
1705 const out = try joinMaybeZ(allocator, separator, slices, true);
1706 return out[0 .. out.len - 1 :0];
1707}
1708
1709fn joinMaybeZ(allocator: *Allocator, separator: []const u8, slices: []const []const u8, zero: bool) ![]u8 {
1495 if (slices.len == 0) return &[0]u8{};1710 if (slices.len == 0) return &[0]u8{};
14961711
1497 const total_len = blk: {1712 const total_len = blk: {
1498 var sum: usize = separator.len * (slices.len - 1);1713 var sum: usize = separator.len * (slices.len - 1);
1499 for (slices) |slice|1714 for (slices) |slice| sum += slice.len;
1500 sum += slice.len;1715 if (zero) sum += 1;
1501 break :blk sum;1716 break :blk sum;
1502 };1717 };
15031718
...@@ -1513,6 +1728,8 @@ pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []cons...@@ -1513,6 +1728,8 @@ pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []cons
1513 buf_index += slice.len;1728 buf_index += slice.len;
1514 }1729 }
15151730
1731 if (zero) buf[buf.len - 1] = 0;
1732
1516 // No need for shrink since buf is exactly the correct size.1733 // No need for shrink since buf is exactly the correct size.
1517 return buf;1734 return buf;
1518}1735}
...@@ -1535,6 +1752,27 @@ test "mem.join" {...@@ -1535,6 +1752,27 @@ test "mem.join" {
1535 }1752 }
1536}1753}
15371754
1755test "mem.joinZ" {
1756 {
1757 const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "b", "c" });
1758 defer testing.allocator.free(str);
1759 testing.expect(eql(u8, str, "a,b,c"));
1760 testing.expectEqual(str[str.len], 0);
1761 }
1762 {
1763 const str = try joinZ(testing.allocator, ",", &[_][]const u8{"a"});
1764 defer testing.allocator.free(str);
1765 testing.expect(eql(u8, str, "a"));
1766 testing.expectEqual(str[str.len], 0);
1767 }
1768 {
1769 const str = try joinZ(testing.allocator, ",", &[_][]const u8{ "a", "", "b", "", "c" });
1770 defer testing.allocator.free(str);
1771 testing.expect(eql(u8, str, "a,,b,,c"));
1772 testing.expectEqual(str[str.len], 0);
1773 }
1774}
1775
1538/// Copies each T from slices into a new slice that exactly holds all the elements.1776/// Copies each T from slices into a new slice that exactly holds all the elements.
1539pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T {1777pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T {
1540 if (slices.len == 0) return &[0]T{};1778 if (slices.len == 0) return &[0]T{};
...@@ -1727,6 +1965,8 @@ fn testWriteIntImpl() void {...@@ -1727,6 +1965,8 @@ fn testWriteIntImpl() void {
1727 }));1965 }));
1728}1966}
17291967
1968/// Returns the smallest number in a slice. O(n).
1969/// `slice` must not be empty.
1730pub fn min(comptime T: type, slice: []const T) T {1970pub fn min(comptime T: type, slice: []const T) T {
1731 var best = slice[0];1971 var best = slice[0];
1732 for (slice[1..]) |item| {1972 for (slice[1..]) |item| {
...@@ -1739,6 +1979,8 @@ test "mem.min" {...@@ -1739,6 +1979,8 @@ test "mem.min" {
1739 testing.expect(min(u8, "abcdefg") == 'a');1979 testing.expect(min(u8, "abcdefg") == 'a');
1740}1980}
17411981
1982/// Returns the largest number in a slice. O(n).
1983/// `slice` must not be empty.
1742pub fn max(comptime T: type, slice: []const T) T {1984pub fn max(comptime T: type, slice: []const T) T {
1743 var best = slice[0];1985 var best = slice[0];
1744 for (slice[1..]) |item| {1986 for (slice[1..]) |item| {
...@@ -1855,7 +2097,7 @@ fn AsBytesReturnType(comptime P: type) type {...@@ -1855,7 +2097,7 @@ fn AsBytesReturnType(comptime P: type) type {
1855}2097}
18562098
1857/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness.2099/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness.
1858pub fn asBytes(ptr: var) AsBytesReturnType(@TypeOf(ptr)) {2100pub fn asBytes(ptr: anytype) AsBytesReturnType(@TypeOf(ptr)) {
1859 const P = @TypeOf(ptr);2101 const P = @TypeOf(ptr);
1860 return @ptrCast(AsBytesReturnType(P), ptr);2102 return @ptrCast(AsBytesReturnType(P), ptr);
1861}2103}
...@@ -1894,8 +2136,8 @@ test "asBytes" {...@@ -1894,8 +2136,8 @@ test "asBytes" {
1894 testing.expect(eql(u8, asBytes(&zero), ""));2136 testing.expect(eql(u8, asBytes(&zero), ""));
1895}2137}
18962138
1897///Given any value, returns a copy of its bytes in an array.2139/// Given any value, returns a copy of its bytes in an array.
1898pub fn toBytes(value: var) [@sizeOf(@TypeOf(value))]u8 {2140pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 {
1899 return asBytes(&value).*;2141 return asBytes(&value).*;
1900}2142}
19012143
...@@ -1928,9 +2170,9 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {...@@ -1928,9 +2170,9 @@ fn BytesAsValueReturnType(comptime T: type, comptime B: type) type {
1928 return if (comptime trait.isConstPtr(B)) *align(alignment) const T else *align(alignment) T;2170 return if (comptime trait.isConstPtr(B)) *align(alignment) const T else *align(alignment) T;
1929}2171}
19302172
1931///Given a pointer to an array of bytes, returns a pointer to a value of the specified type2173/// Given a pointer to an array of bytes, returns a pointer to a value of the specified type
1932/// backed by those bytes, preserving constness.2174/// backed by those bytes, preserving constness.
1933pub fn bytesAsValue(comptime T: type, bytes: var) BytesAsValueReturnType(T, @TypeOf(bytes)) {2175pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) {
1934 return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes);2176 return @ptrCast(BytesAsValueReturnType(T, @TypeOf(bytes)), bytes);
1935}2177}
19362178
...@@ -1971,9 +2213,9 @@ test "bytesAsValue" {...@@ -1971,9 +2213,9 @@ test "bytesAsValue" {
1971 testing.expect(meta.eql(inst, inst2.*));2213 testing.expect(meta.eql(inst, inst2.*));
1972}2214}
19732215
1974///Given a pointer to an array of bytes, returns a value of the specified type backed by a2216/// Given a pointer to an array of bytes, returns a value of the specified type backed by a
1975/// copy of those bytes.2217/// copy of those bytes.
1976pub fn bytesToValue(comptime T: type, bytes: var) T {2218pub fn bytesToValue(comptime T: type, bytes: anytype) T {
1977 return bytesAsValue(T, bytes).*;2219 return bytesAsValue(T, bytes).*;
1978}2220}
1979test "bytesToValue" {2221test "bytesToValue" {
...@@ -2001,7 +2243,7 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {...@@ -2001,7 +2243,7 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {
2001 return if (trait.isConstPtr(bytesType)) []align(alignment) const T else []align(alignment) T;2243 return if (trait.isConstPtr(bytesType)) []align(alignment) const T else []align(alignment) T;
2002}2244}
20032245
2004pub fn bytesAsSlice(comptime T: type, bytes: var) BytesAsSliceReturnType(T, @TypeOf(bytes)) {2246pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) {
2005 // let's not give an undefined pointer to @ptrCast2247 // let's not give an undefined pointer to @ptrCast
2006 // it may be equal to zero and fail a null check2248 // it may be equal to zero and fail a null check
2007 if (bytes.len == 0) {2249 if (bytes.len == 0) {
...@@ -2080,7 +2322,7 @@ fn SliceAsBytesReturnType(comptime sliceType: type) type {...@@ -2080,7 +2322,7 @@ fn SliceAsBytesReturnType(comptime sliceType: type) type {
2080 return if (trait.isConstPtr(sliceType)) []align(alignment) const u8 else []align(alignment) u8;2322 return if (trait.isConstPtr(sliceType)) []align(alignment) const u8 else []align(alignment) u8;
2081}2323}
20822324
2083pub fn sliceAsBytes(slice: var) SliceAsBytesReturnType(@TypeOf(slice)) {2325pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) {
2084 const Slice = @TypeOf(slice);2326 const Slice = @TypeOf(slice);
20852327
2086 // let's not give an undefined pointer to @ptrCast2328 // let's not give an undefined pointer to @ptrCast
...@@ -2190,6 +2432,15 @@ test "alignForward" {...@@ -2190,6 +2432,15 @@ test "alignForward" {
2190 testing.expect(alignForward(17, 8) == 24);2432 testing.expect(alignForward(17, 8) == 24);
2191}2433}
21922434
2435/// Round an address up to the previous aligned address
2436/// Unlike `alignBackward`, `alignment` can be any positive number, not just a power of 2.
2437pub fn alignBackwardAnyAlign(i: usize, alignment: usize) usize {
2438 if (@popCount(usize, alignment) == 1)
2439 return alignBackward(i, alignment);
2440 assert(alignment != 0);
2441 return i - @mod(i, alignment);
2442}
2443
2193/// Round an address up to the previous aligned address2444/// Round an address up to the previous aligned address
2194/// The alignment must be a power of 2 and greater than 0.2445/// The alignment must be a power of 2 and greater than 0.
2195pub fn alignBackward(addr: usize, alignment: usize) usize {2446pub fn alignBackward(addr: usize, alignment: usize) usize {
...@@ -2206,6 +2457,19 @@ pub fn alignBackwardGeneric(comptime T: type, addr: T, alignment: T) T {...@@ -2206,6 +2457,19 @@ pub fn alignBackwardGeneric(comptime T: type, addr: T, alignment: T) T {
2206 return addr & ~(alignment - 1);2457 return addr & ~(alignment - 1);
2207}2458}
22082459
2460/// Returns whether `alignment` is a valid alignment, meaning it is
2461/// a positive power of 2.
2462pub fn isValidAlign(alignment: u29) bool {
2463 return @popCount(u29, alignment) == 1;
2464}
2465
2466pub fn isAlignedAnyAlign(i: usize, alignment: usize) bool {
2467 if (@popCount(usize, alignment) == 1)
2468 return isAligned(i, alignment);
2469 assert(alignment != 0);
2470 return 0 == @mod(i, alignment);
2471}
2472
2209/// Given an address and an alignment, return true if the address is a multiple of the alignment2473/// Given an address and an alignment, return true if the address is a multiple of the alignment
2210/// The alignment must be a power of 2 and greater than 0.2474/// The alignment must be a power of 2 and greater than 0.
2211pub fn isAligned(addr: usize, alignment: usize) bool {2475pub fn isAligned(addr: usize, alignment: usize) bool {
lib/std/meta.zig+93-10
...@@ -6,10 +6,11 @@ const math = std.math;...@@ -6,10 +6,11 @@ const math = std.math;
6const testing = std.testing;6const testing = std.testing;
77
8pub const trait = @import("meta/trait.zig");8pub const trait = @import("meta/trait.zig");
9pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags;
910
10const TypeInfo = builtin.TypeInfo;11const TypeInfo = builtin.TypeInfo;
1112
12pub fn tagName(v: var) []const u8 {13pub fn tagName(v: anytype) []const u8 {
13 const T = @TypeOf(v);14 const T = @TypeOf(v);
14 switch (@typeInfo(T)) {15 switch (@typeInfo(T)) {
15 .ErrorSet => return @errorName(v),16 .ErrorSet => return @errorName(v),
...@@ -250,7 +251,7 @@ test "std.meta.containerLayout" {...@@ -250,7 +251,7 @@ test "std.meta.containerLayout" {
250 testing.expect(containerLayout(U3) == .Extern);251 testing.expect(containerLayout(U3) == .Extern);
251}252}
252253
253pub fn declarations(comptime T: type) []TypeInfo.Declaration {254pub fn declarations(comptime T: type) []const TypeInfo.Declaration {
254 return switch (@typeInfo(T)) {255 return switch (@typeInfo(T)) {
255 .Struct => |info| info.decls,256 .Struct => |info| info.decls,
256 .Enum => |info| info.decls,257 .Enum => |info| info.decls,
...@@ -274,7 +275,7 @@ test "std.meta.declarations" {...@@ -274,7 +275,7 @@ test "std.meta.declarations" {
274 fn a() void {}275 fn a() void {}
275 };276 };
276277
277 const decls = comptime [_][]TypeInfo.Declaration{278 const decls = comptime [_][]const TypeInfo.Declaration{
278 declarations(E1),279 declarations(E1),
279 declarations(S1),280 declarations(S1),
280 declarations(U1),281 declarations(U1),
...@@ -323,10 +324,10 @@ test "std.meta.declarationInfo" {...@@ -323,10 +324,10 @@ test "std.meta.declarationInfo" {
323}324}
324325
325pub fn fields(comptime T: type) switch (@typeInfo(T)) {326pub fn fields(comptime T: type) switch (@typeInfo(T)) {
326 .Struct => []TypeInfo.StructField,327 .Struct => []const TypeInfo.StructField,
327 .Union => []TypeInfo.UnionField,328 .Union => []const TypeInfo.UnionField,
328 .ErrorSet => []TypeInfo.Error,329 .ErrorSet => []const TypeInfo.Error,
329 .Enum => []TypeInfo.EnumField,330 .Enum => []const TypeInfo.EnumField,
330 else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),331 else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
331} {332} {
332 return switch (@typeInfo(T)) {333 return switch (@typeInfo(T)) {
...@@ -430,7 +431,7 @@ test "std.meta.TagType" {...@@ -430,7 +431,7 @@ test "std.meta.TagType" {
430}431}
431432
432///Returns the active tag of a tagged union433///Returns the active tag of a tagged union
433pub fn activeTag(u: var) @TagType(@TypeOf(u)) {434pub fn activeTag(u: anytype) @TagType(@TypeOf(u)) {
434 const T = @TypeOf(u);435 const T = @TypeOf(u);
435 return @as(@TagType(T), u);436 return @as(@TagType(T), u);
436}437}
...@@ -480,7 +481,7 @@ test "std.meta.TagPayloadType" {...@@ -480,7 +481,7 @@ test "std.meta.TagPayloadType" {
480481
481/// Compares two of any type for equality. Containers are compared on a field-by-field basis,482/// Compares two of any type for equality. Containers are compared on a field-by-field basis,
482/// where possible. Pointers are not followed.483/// where possible. Pointers are not followed.
483pub fn eql(a: var, b: @TypeOf(a)) bool {484pub fn eql(a: anytype, b: @TypeOf(a)) bool {
484 const T = @TypeOf(a);485 const T = @TypeOf(a);
485486
486 switch (@typeInfo(T)) {487 switch (@typeInfo(T)) {
...@@ -627,7 +628,7 @@ test "intToEnum with error return" {...@@ -627,7 +628,7 @@ test "intToEnum with error return" {
627628
628pub const IntToEnumError = error{InvalidEnumTag};629pub const IntToEnumError = error{InvalidEnumTag};
629630
630pub fn intToEnum(comptime Tag: type, tag_int: var) IntToEnumError!Tag {631pub fn intToEnum(comptime Tag: type, tag_int: anytype) IntToEnumError!Tag {
631 inline for (@typeInfo(Tag).Enum.fields) |f| {632 inline for (@typeInfo(Tag).Enum.fields) |f| {
632 const this_tag_value = @field(Tag, f.name);633 const this_tag_value = @field(Tag, f.name);
633 if (tag_int == @enumToInt(this_tag_value)) {634 if (tag_int == @enumToInt(this_tag_value)) {
...@@ -693,3 +694,85 @@ pub fn Vector(comptime len: u32, comptime child: type) type {...@@ -693,3 +694,85 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
693 },694 },
694 });695 });
695}696}
697
698/// Given a type and value, cast the value to the type as c would.
699/// This is for translate-c and is not intended for general use.
700pub fn cast(comptime DestType: type, target: anytype) DestType {
701 const TargetType = @TypeOf(target);
702 switch (@typeInfo(DestType)) {
703 .Pointer => {
704 switch (@typeInfo(TargetType)) {
705 .Int, .ComptimeInt => {
706 return @intToPtr(DestType, target);
707 },
708 .Pointer => |ptr| {
709 return @ptrCast(DestType, @alignCast(ptr.alignment, target));
710 },
711 .Optional => |opt| {
712 if (@typeInfo(opt.child) == .Pointer) {
713 return @ptrCast(DestType, @alignCast(@alignOf(opt.child.Child), target));
714 }
715 },
716 else => {},
717 }
718 },
719 .Optional => |opt| {
720 if (@typeInfo(opt.child) == .Pointer) {
721 switch (@typeInfo(TargetType)) {
722 .Int, .ComptimeInt => {
723 return @intToPtr(DestType, target);
724 },
725 .Pointer => |ptr| {
726 return @ptrCast(DestType, @alignCast(ptr.alignment, target));
727 },
728 .Optional => |target_opt| {
729 if (@typeInfo(target_opt.child) == .Pointer) {
730 return @ptrCast(DestType, @alignCast(@alignOf(target_opt.child.Child), target));
731 }
732 },
733 else => {},
734 }
735 }
736 },
737 .Enum, .EnumLiteral => {
738 if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
739 return @intToEnum(DestType, target);
740 }
741 },
742 .Int, .ComptimeInt => {
743 switch (@typeInfo(TargetType)) {
744 .Pointer => {
745 return @as(DestType, @ptrToInt(target));
746 },
747 .Optional => |opt| {
748 if (@typeInfo(opt.child) == .Pointer) {
749 return @as(DestType, @ptrToInt(target));
750 }
751 },
752 .Enum, .EnumLiteral => {
753 return @as(DestType, @enumToInt(target));
754 },
755 else => {},
756 }
757 },
758 else => {},
759 }
760 return @as(DestType, target);
761}
762
763test "std.meta.cast" {
764 const E = enum(u2) {
765 Zero,
766 One,
767 Two,
768 };
769
770 var i = @as(i64, 10);
771
772 testing.expect(cast(?*c_void, 0) == @intToPtr(?*c_void, 0));
773 testing.expect(cast(*u8, 16) == @intToPtr(*u8, 16));
774 testing.expect(cast(u64, @as(u32, 10)) == @as(u64, 10));
775 testing.expect(cast(E, 1) == .One);
776 testing.expect(cast(u8, E.Two) == 2);
777 testing.expect(cast(*u64, &i).* == @as(u64, 10));
778}
lib/std/meta/trailer_flags.zig created+145
...@@ -0,0 +1,145 @@
1const std = @import("../std.zig");
2const meta = std.meta;
3const testing = std.testing;
4const mem = std.mem;
5const assert = std.debug.assert;
6
7/// This is useful for saving memory when allocating an object that has many
8/// optional components. The optional objects are allocated sequentially in
9/// memory, and a single integer is used to represent each optional object
10/// and whether it is present based on each corresponding bit.
11pub fn TrailerFlags(comptime Fields: type) type {
12 return struct {
13 bits: Int,
14
15 pub const Int = @Type(.{ .Int = .{ .bits = bit_count, .is_signed = false } });
16 pub const bit_count = @typeInfo(Fields).Struct.fields.len;
17
18 pub const Self = @This();
19
20 pub fn has(self: Self, comptime name: []const u8) bool {
21 const field_index = meta.fieldIndex(Fields, name).?;
22 return (self.bits & (1 << field_index)) != 0;
23 }
24
25 pub fn get(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) ?Field(name) {
26 if (!self.has(name))
27 return null;
28 return self.ptrConst(p, name).*;
29 }
30
31 pub fn setFlag(self: *Self, comptime name: []const u8) void {
32 const field_index = meta.fieldIndex(Fields, name).?;
33 self.bits |= 1 << field_index;
34 }
35
36 /// `fields` is a struct with each field set to an optional value.
37 /// Missing fields are assumed to be `null`.
38 /// Only the non-null bits are observed and are used to set the flag bits.
39 pub fn init(fields: anytype) Self {
40 var self: Self = .{ .bits = 0 };
41 inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| {
42 const opt: ?Field(field.name) = @field(fields, field.name);
43 const field_index = meta.fieldIndex(Fields, field.name).?;
44 self.bits |= @as(Int, @boolToInt(opt != null)) << field_index;
45 }
46 return self;
47 }
48
49 /// `fields` is a struct with each field set to an optional value (same as `init`).
50 /// Missing fields are assumed to be `null`.
51 pub fn setMany(self: Self, p: [*]align(@alignOf(Fields)) u8, fields: anytype) void {
52 inline for (@typeInfo(@TypeOf(fields)).Struct.fields) |field| {
53 const opt: ?Field(field.name) = @field(fields, field.name);
54 if (opt) |value| {
55 self.set(p, field.name, value);
56 }
57 }
58 }
59
60 pub fn set(
61 self: Self,
62 p: [*]align(@alignOf(Fields)) u8,
63 comptime name: []const u8,
64 value: Field(name),
65 ) void {
66 self.ptr(p, name).* = value;
67 }
68
69 pub fn ptr(self: Self, p: [*]align(@alignOf(Fields)) u8, comptime name: []const u8) *Field(name) {
70 if (@sizeOf(Field(name)) == 0)
71 return undefined;
72 const off = self.offset(p, name);
73 return @ptrCast(*Field(name), @alignCast(@alignOf(Field(name)), p + off));
74 }
75
76 pub fn ptrConst(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) *const Field(name) {
77 if (@sizeOf(Field(name)) == 0)
78 return undefined;
79 const off = self.offset(p, name);
80 return @ptrCast(*const Field(name), @alignCast(@alignOf(Field(name)), p + off));
81 }
82
83 pub fn offset(self: Self, p: [*]align(@alignOf(Fields)) const u8, comptime name: []const u8) usize {
84 var off: usize = 0;
85 inline for (@typeInfo(Fields).Struct.fields) |field, i| {
86 const active = (self.bits & (1 << i)) != 0;
87 if (comptime mem.eql(u8, field.name, name)) {
88 assert(active);
89 return mem.alignForwardGeneric(usize, off, @alignOf(field.field_type));
90 } else if (active) {
91 off = mem.alignForwardGeneric(usize, off, @alignOf(field.field_type));
92 off += @sizeOf(field.field_type);
93 }
94 }
95 @compileError("no field named " ++ name ++ " in type " ++ @typeName(Fields));
96 }
97
98 pub fn Field(comptime name: []const u8) type {
99 return meta.fieldInfo(Fields, name).field_type;
100 }
101
102 pub fn sizeInBytes(self: Self) usize {
103 var off: usize = 0;
104 inline for (@typeInfo(Fields).Struct.fields) |field, i| {
105 if (@sizeOf(field.field_type) == 0)
106 continue;
107 if ((self.bits & (1 << i)) != 0) {
108 off = mem.alignForwardGeneric(usize, off, @alignOf(field.field_type));
109 off += @sizeOf(field.field_type);
110 }
111 }
112 return off;
113 }
114 };
115}
116
117test "TrailerFlags" {
118 const Flags = TrailerFlags(struct {
119 a: i32,
120 b: bool,
121 c: u64,
122 });
123 var flags = Flags.init(.{
124 .b = true,
125 .c = 1234,
126 });
127 const slice = try testing.allocator.allocAdvanced(u8, 8, flags.sizeInBytes(), .exact);
128 defer testing.allocator.free(slice);
129
130 flags.set(slice.ptr, "b", false);
131 flags.set(slice.ptr, "c", 12345678);
132
133 testing.expect(flags.get(slice.ptr, "a") == null);
134 testing.expect(!flags.get(slice.ptr, "b").?);
135 testing.expect(flags.get(slice.ptr, "c").? == 12345678);
136
137 flags.setMany(slice.ptr, .{
138 .b = true,
139 .c = 5678,
140 });
141
142 testing.expect(flags.get(slice.ptr, "a") == null);
143 testing.expect(flags.get(slice.ptr, "b").?);
144 testing.expect(flags.get(slice.ptr, "c").? == 5678);
145}
lib/std/meta/trait.zig+17-4
...@@ -9,7 +9,7 @@ const meta = @import("../meta.zig");...@@ -9,7 +9,7 @@ const meta = @import("../meta.zig");
99
10pub const TraitFn = fn (type) bool;10pub const TraitFn = fn (type) bool;
1111
12pub fn multiTrait(comptime traits: var) TraitFn {12pub fn multiTrait(comptime traits: anytype) TraitFn {
13 const Closure = struct {13 const Closure = struct {
14 pub fn trait(comptime T: type) bool {14 pub fn trait(comptime T: type) bool {
15 inline for (traits) |t|15 inline for (traits) |t|
...@@ -342,7 +342,20 @@ test "std.meta.trait.isContainer" {...@@ -342,7 +342,20 @@ test "std.meta.trait.isContainer" {
342 testing.expect(!isContainer(u8));342 testing.expect(!isContainer(u8));
343}343}
344344
345pub fn hasDecls(comptime T: type, comptime names: var) bool {345pub fn isTuple(comptime T: type) bool {
346 return is(.Struct)(T) and @typeInfo(T).Struct.is_tuple;
347}
348
349test "std.meta.trait.isTuple" {
350 const t1 = struct {};
351 const t2 = .{ .a = 0 };
352 const t3 = .{ 1, 2, 3 };
353 testing.expect(!isTuple(t1));
354 testing.expect(!isTuple(@TypeOf(t2)));
355 testing.expect(isTuple(@TypeOf(t3)));
356}
357
358pub fn hasDecls(comptime T: type, comptime names: anytype) bool {
346 inline for (names) |name| {359 inline for (names) |name| {
347 if (!@hasDecl(T, name))360 if (!@hasDecl(T, name))
348 return false;361 return false;
...@@ -368,7 +381,7 @@ test "std.meta.trait.hasDecls" {...@@ -368,7 +381,7 @@ test "std.meta.trait.hasDecls" {
368 testing.expect(!hasDecls(TestStruct2, tuple));381 testing.expect(!hasDecls(TestStruct2, tuple));
369}382}
370383
371pub fn hasFields(comptime T: type, comptime names: var) bool {384pub fn hasFields(comptime T: type, comptime names: anytype) bool {
372 inline for (names) |name| {385 inline for (names) |name| {
373 if (!@hasField(T, name))386 if (!@hasField(T, name))
374 return false;387 return false;
...@@ -394,7 +407,7 @@ test "std.meta.trait.hasFields" {...@@ -394,7 +407,7 @@ test "std.meta.trait.hasFields" {
394 testing.expect(!hasFields(TestStruct2, .{ "a", "b", "useless" }));407 testing.expect(!hasFields(TestStruct2, .{ "a", "b", "useless" }));
395}408}
396409
397pub fn hasFunctions(comptime T: type, comptime names: var) bool {410pub fn hasFunctions(comptime T: type, comptime names: anytype) bool {
398 inline for (names) |name| {411 inline for (names) |name| {
399 if (!hasFn(name)(T))412 if (!hasFn(name)(T))
400 return false;413 return false;
lib/std/net.zig+4-4
...@@ -427,7 +427,7 @@ pub const Address = extern union {...@@ -427,7 +427,7 @@ pub const Address = extern union {
427 self: Address,427 self: Address,
428 comptime fmt: []const u8,428 comptime fmt: []const u8,
429 options: std.fmt.FormatOptions,429 options: std.fmt.FormatOptions,
430 out_stream: var,430 out_stream: anytype,
431 ) !void {431 ) !void {
432 switch (self.any.family) {432 switch (self.any.family) {
433 os.AF_INET => {433 os.AF_INET => {
...@@ -682,7 +682,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*...@@ -682,7 +682,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*
682682
683 if (info.canonname) |n| {683 if (info.canonname) |n| {
684 if (result.canon_name == null) {684 if (result.canon_name == null) {
685 result.canon_name = try mem.dupe(arena, u8, mem.spanZ(n));685 result.canon_name = try arena.dupe(u8, mem.spanZ(n));
686 }686 }
687 }687 }
688 i += 1;688 i += 1;
...@@ -1404,8 +1404,8 @@ fn resMSendRc(...@@ -1404,8 +1404,8 @@ fn resMSendRc(
14041404
1405fn dnsParse(1405fn dnsParse(
1406 r: []const u8,1406 r: []const u8,
1407 ctx: var,1407 ctx: anytype,
1408 comptime callback: var,1408 comptime callback: anytype,
1409) !void {1409) !void {
1410 // This implementation is ported from musl libc.1410 // This implementation is ported from musl libc.
1411 // A more idiomatic "ziggy" implementation would be welcome.1411 // A more idiomatic "ziggy" implementation would be welcome.
lib/std/os.zig+241-40
...@@ -300,6 +300,10 @@ pub const ReadError = error{...@@ -300,6 +300,10 @@ pub const ReadError = error{
300 /// This error occurs when no global event loop is configured,300 /// This error occurs when no global event loop is configured,
301 /// and reading from the file descriptor would block.301 /// and reading from the file descriptor would block.
302 WouldBlock,302 WouldBlock,
303
304 /// In WASI, this error occurs when the file descriptor does
305 /// not hold the required rights to read from it.
306 AccessDenied,
303} || UnexpectedError;307} || UnexpectedError;
304308
305/// Returns the number of bytes that were read, which can be less than309/// Returns the number of bytes that were read, which can be less than
...@@ -335,6 +339,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {...@@ -335,6 +339,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
335 wasi.ENOMEM => return error.SystemResources,339 wasi.ENOMEM => return error.SystemResources,
336 wasi.ECONNRESET => return error.ConnectionResetByPeer,340 wasi.ECONNRESET => return error.ConnectionResetByPeer,
337 wasi.ETIMEDOUT => return error.ConnectionTimedOut,341 wasi.ETIMEDOUT => return error.ConnectionTimedOut,
342 wasi.ENOTCAPABLE => return error.AccessDenied,
338 else => |err| return unexpectedErrno(err),343 else => |err| return unexpectedErrno(err),
339 }344 }
340 }345 }
...@@ -402,6 +407,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {...@@ -402,6 +407,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
402 wasi.EISDIR => return error.IsDir,407 wasi.EISDIR => return error.IsDir,
403 wasi.ENOBUFS => return error.SystemResources,408 wasi.ENOBUFS => return error.SystemResources,
404 wasi.ENOMEM => return error.SystemResources,409 wasi.ENOMEM => return error.SystemResources,
410 wasi.ENOTCAPABLE => return error.AccessDenied,
405 else => |err| return unexpectedErrno(err),411 else => |err| return unexpectedErrno(err),
406 }412 }
407 }413 }
...@@ -466,6 +472,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {...@@ -466,6 +472,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
466 wasi.ENXIO => return error.Unseekable,472 wasi.ENXIO => return error.Unseekable,
467 wasi.ESPIPE => return error.Unseekable,473 wasi.ESPIPE => return error.Unseekable,
468 wasi.EOVERFLOW => return error.Unseekable,474 wasi.EOVERFLOW => return error.Unseekable,
475 wasi.ENOTCAPABLE => return error.AccessDenied,
469 else => |err| return unexpectedErrno(err),476 else => |err| return unexpectedErrno(err),
470 }477 }
471 }478 }
...@@ -500,8 +507,11 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {...@@ -500,8 +507,11 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
500pub const TruncateError = error{507pub const TruncateError = error{
501 FileTooBig,508 FileTooBig,
502 InputOutput,509 InputOutput,
503 CannotTruncate,
504 FileBusy,510 FileBusy,
511
512 /// In WASI, this error occurs when the file descriptor does
513 /// not hold the required rights to call `ftruncate` on it.
514 AccessDenied,
505} || UnexpectedError;515} || UnexpectedError;
506516
507pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {517pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
...@@ -522,7 +532,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {...@@ -522,7 +532,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
522 switch (rc) {532 switch (rc) {
523 .SUCCESS => return,533 .SUCCESS => return,
524 .INVALID_HANDLE => unreachable, // Handle not open for writing534 .INVALID_HANDLE => unreachable, // Handle not open for writing
525 .ACCESS_DENIED => return error.CannotTruncate,535 .ACCESS_DENIED => return error.AccessDenied,
526 else => return windows.unexpectedStatus(rc),536 else => return windows.unexpectedStatus(rc),
527 }537 }
528 }538 }
...@@ -532,10 +542,11 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {...@@ -532,10 +542,11 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
532 wasi.EINTR => unreachable,542 wasi.EINTR => unreachable,
533 wasi.EFBIG => return error.FileTooBig,543 wasi.EFBIG => return error.FileTooBig,
534 wasi.EIO => return error.InputOutput,544 wasi.EIO => return error.InputOutput,
535 wasi.EPERM => return error.CannotTruncate,545 wasi.EPERM => return error.AccessDenied,
536 wasi.ETXTBSY => return error.FileBusy,546 wasi.ETXTBSY => return error.FileBusy,
537 wasi.EBADF => unreachable, // Handle not open for writing547 wasi.EBADF => unreachable, // Handle not open for writing
538 wasi.EINVAL => unreachable, // Handle not open for writing548 wasi.EINVAL => unreachable, // Handle not open for writing
549 wasi.ENOTCAPABLE => return error.AccessDenied,
539 else => |err| return unexpectedErrno(err),550 else => |err| return unexpectedErrno(err),
540 }551 }
541 }552 }
...@@ -554,7 +565,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {...@@ -554,7 +565,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
554 EINTR => continue,565 EINTR => continue,
555 EFBIG => return error.FileTooBig,566 EFBIG => return error.FileTooBig,
556 EIO => return error.InputOutput,567 EIO => return error.InputOutput,
557 EPERM => return error.CannotTruncate,568 EPERM => return error.AccessDenied,
558 ETXTBSY => return error.FileBusy,569 ETXTBSY => return error.FileBusy,
559 EBADF => unreachable, // Handle not open for writing570 EBADF => unreachable, // Handle not open for writing
560 EINVAL => unreachable, // Handle not open for writing571 EINVAL => unreachable, // Handle not open for writing
...@@ -604,6 +615,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {...@@ -604,6 +615,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
604 wasi.ENXIO => return error.Unseekable,615 wasi.ENXIO => return error.Unseekable,
605 wasi.ESPIPE => return error.Unseekable,616 wasi.ESPIPE => return error.Unseekable,
606 wasi.EOVERFLOW => return error.Unseekable,617 wasi.EOVERFLOW => return error.Unseekable,
618 wasi.ENOTCAPABLE => return error.AccessDenied,
607 else => |err| return unexpectedErrno(err),619 else => |err| return unexpectedErrno(err),
608 }620 }
609 }621 }
...@@ -641,6 +653,9 @@ pub const WriteError = error{...@@ -641,6 +653,9 @@ pub const WriteError = error{
641 FileTooBig,653 FileTooBig,
642 InputOutput,654 InputOutput,
643 NoSpaceLeft,655 NoSpaceLeft,
656
657 /// In WASI, this error may occur when the file descriptor does
658 /// not hold the required rights to write to it.
644 AccessDenied,659 AccessDenied,
645 BrokenPipe,660 BrokenPipe,
646 SystemResources,661 SystemResources,
...@@ -697,6 +712,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {...@@ -697,6 +712,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
697 wasi.ENOSPC => return error.NoSpaceLeft,712 wasi.ENOSPC => return error.NoSpaceLeft,
698 wasi.EPERM => return error.AccessDenied,713 wasi.EPERM => return error.AccessDenied,
699 wasi.EPIPE => return error.BrokenPipe,714 wasi.EPIPE => return error.BrokenPipe,
715 wasi.ENOTCAPABLE => return error.AccessDenied,
700 else => |err| return unexpectedErrno(err),716 else => |err| return unexpectedErrno(err),
701 }717 }
702 }718 }
...@@ -774,6 +790,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {...@@ -774,6 +790,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
774 wasi.ENOSPC => return error.NoSpaceLeft,790 wasi.ENOSPC => return error.NoSpaceLeft,
775 wasi.EPERM => return error.AccessDenied,791 wasi.EPERM => return error.AccessDenied,
776 wasi.EPIPE => return error.BrokenPipe,792 wasi.EPIPE => return error.BrokenPipe,
793 wasi.ENOTCAPABLE => return error.AccessDenied,
777 else => |err| return unexpectedErrno(err),794 else => |err| return unexpectedErrno(err),
778 }795 }
779 }796 }
...@@ -856,6 +873,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {...@@ -856,6 +873,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
856 wasi.ENXIO => return error.Unseekable,873 wasi.ENXIO => return error.Unseekable,
857 wasi.ESPIPE => return error.Unseekable,874 wasi.ESPIPE => return error.Unseekable,
858 wasi.EOVERFLOW => return error.Unseekable,875 wasi.EOVERFLOW => return error.Unseekable,
876 wasi.ENOTCAPABLE => return error.AccessDenied,
859 else => |err| return unexpectedErrno(err),877 else => |err| return unexpectedErrno(err),
860 }878 }
861 }879 }
...@@ -949,6 +967,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz...@@ -949,6 +967,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
949 wasi.ENXIO => return error.Unseekable,967 wasi.ENXIO => return error.Unseekable,
950 wasi.ESPIPE => return error.Unseekable,968 wasi.ESPIPE => return error.Unseekable,
951 wasi.EOVERFLOW => return error.Unseekable,969 wasi.EOVERFLOW => return error.Unseekable,
970 wasi.ENOTCAPABLE => return error.AccessDenied,
952 else => |err| return unexpectedErrno(err),971 else => |err| return unexpectedErrno(err),
953 }972 }
954 }973 }
...@@ -984,6 +1003,8 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz...@@ -984,6 +1003,8 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz
984}1003}
9851004
986pub const OpenError = error{1005pub const OpenError = error{
1006 /// In WASI, this error may occur when the file descriptor does
1007 /// not hold the required rights to open a new resource relative to it.
987 AccessDenied,1008 AccessDenied,
988 SymLinkLoop,1009 SymLinkLoop,
989 ProcessFdQuotaExceeded,1010 ProcessFdQuotaExceeded,
...@@ -1113,6 +1134,7 @@ pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, oflags: oflags_t, fdflags...@@ -1113,6 +1134,7 @@ pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, oflags: oflags_t, fdflags
1113 wasi.EPERM => return error.AccessDenied,1134 wasi.EPERM => return error.AccessDenied,
1114 wasi.EEXIST => return error.PathAlreadyExists,1135 wasi.EEXIST => return error.PathAlreadyExists,
1115 wasi.EBUSY => return error.DeviceBusy,1136 wasi.EBUSY => return error.DeviceBusy,
1137 wasi.ENOTCAPABLE => return error.AccessDenied,
1116 else => |err| return unexpectedErrno(err),1138 else => |err| return unexpectedErrno(err),
1117 }1139 }
1118 }1140 }
...@@ -1499,6 +1521,8 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {...@@ -1499,6 +1521,8 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
1499}1521}
15001522
1501pub const SymLinkError = error{1523pub const SymLinkError = error{
1524 /// In WASI, this error may occur when the file descriptor does
1525 /// not hold the required rights to create a new symbolic link relative to it.
1502 AccessDenied,1526 AccessDenied,
1503 DiskQuota,1527 DiskQuota,
1504 PathAlreadyExists,1528 PathAlreadyExists,
...@@ -1520,15 +1544,17 @@ pub const SymLinkError = error{...@@ -1520,15 +1544,17 @@ pub const SymLinkError = error{
1520/// If `sym_link_path` exists, it will not be overwritten.1544/// If `sym_link_path` exists, it will not be overwritten.
1521/// See also `symlinkC` and `symlinkW`.1545/// See also `symlinkC` and `symlinkW`.
1522pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {1546pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
1547 if (builtin.os.tag == .wasi) {
1548 @compileError("symlink is not supported in WASI; use symlinkat instead");
1549 }
1523 if (builtin.os.tag == .windows) {1550 if (builtin.os.tag == .windows) {
1524 const target_path_w = try windows.sliceToPrefixedFileW(target_path);1551 const target_path_w = try windows.sliceToPrefixedFileW(target_path);
1525 const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);1552 const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
1526 return windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, 0);1553 return windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, 0);
1527 } else {
1528 const target_path_c = try toPosixPath(target_path);
1529 const sym_link_path_c = try toPosixPath(sym_link_path);
1530 return symlinkZ(&target_path_c, &sym_link_path_c);
1531 }1554 }
1555 const target_path_c = try toPosixPath(target_path);
1556 const sym_link_path_c = try toPosixPath(sym_link_path);
1557 return symlinkZ(&target_path_c, &sym_link_path_c);
1532}1558}
15331559
1534pub const symlinkC = @compileError("deprecated: renamed to symlinkZ");1560pub const symlinkC = @compileError("deprecated: renamed to symlinkZ");
...@@ -1561,15 +1587,66 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin...@@ -1561,15 +1587,66 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin
1561 }1587 }
1562}1588}
15631589
1590/// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string
1591/// `target_path` **relative** to `newdirfd` directory handle.
1592/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
1593/// one; the latter case is known as a dangling link.
1594/// If `sym_link_path` exists, it will not be overwritten.
1595/// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`.
1564pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {1596pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
1597 if (builtin.os.tag == .wasi) {
1598 return symlinkatWasi(target_path, newdirfd, sym_link_path);
1599 }
1600 if (builtin.os.tag == .windows) {
1601 const target_path_w = try windows.sliceToPrefixedFileW(target_path);
1602 const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
1603 return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path_w.span().ptr);
1604 }
1565 const target_path_c = try toPosixPath(target_path);1605 const target_path_c = try toPosixPath(target_path);
1566 const sym_link_path_c = try toPosixPath(sym_link_path);1606 const sym_link_path_c = try toPosixPath(sym_link_path);
1567 return symlinkatZ(target_path_c, newdirfd, sym_link_path_c);1607 return symlinkatZ(&target_path_c, newdirfd, &sym_link_path_c);
1568}1608}
15691609
1570pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ");1610pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ");
15711611
1612/// WASI-only. The same as `symlinkat` but targeting WASI.
1613/// See also `symlinkat`.
1614pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
1615 switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) {
1616 wasi.ESUCCESS => {},
1617 wasi.EFAULT => unreachable,
1618 wasi.EINVAL => unreachable,
1619 wasi.EACCES => return error.AccessDenied,
1620 wasi.EPERM => return error.AccessDenied,
1621 wasi.EDQUOT => return error.DiskQuota,
1622 wasi.EEXIST => return error.PathAlreadyExists,
1623 wasi.EIO => return error.FileSystem,
1624 wasi.ELOOP => return error.SymLinkLoop,
1625 wasi.ENAMETOOLONG => return error.NameTooLong,
1626 wasi.ENOENT => return error.FileNotFound,
1627 wasi.ENOTDIR => return error.NotDir,
1628 wasi.ENOMEM => return error.SystemResources,
1629 wasi.ENOSPC => return error.NoSpaceLeft,
1630 wasi.EROFS => return error.ReadOnlyFileSystem,
1631 wasi.ENOTCAPABLE => return error.AccessDenied,
1632 else => |err| return unexpectedErrno(err),
1633 }
1634}
1635
1636/// Windows-only. The same as `symlinkat` except the paths are null-terminated, WTF-16 encoded.
1637/// See also `symlinkat`.
1638pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymLinkError!void {
1639 @compileError("TODO implement on Windows");
1640}
1641
1642/// The same as `symlinkat` except the parameters are null-terminated pointers.
1643/// See also `symlinkat`.
1572pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {1644pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
1645 if (builtin.os.tag == .windows) {
1646 const target_path_w = try windows.cStrToPrefixedFileW(target_path);
1647 const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path);
1648 return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path.span().ptr);
1649 }
1573 switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {1650 switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
1574 0 => return,1651 0 => return,
1575 EFAULT => unreachable,1652 EFAULT => unreachable,
...@@ -1592,6 +1669,9 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:...@@ -1592,6 +1669,9 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:
15921669
1593pub const UnlinkError = error{1670pub const UnlinkError = error{
1594 FileNotFound,1671 FileNotFound,
1672
1673 /// In WASI, this error may occur when the file descriptor does
1674 /// not hold the required rights to unlink a resource by path relative to it.
1595 AccessDenied,1675 AccessDenied,
1596 FileBusy,1676 FileBusy,
1597 FileSystem,1677 FileSystem,
...@@ -1613,7 +1693,9 @@ pub const UnlinkError = error{...@@ -1613,7 +1693,9 @@ pub const UnlinkError = error{
1613/// Delete a name and possibly the file it refers to.1693/// Delete a name and possibly the file it refers to.
1614/// See also `unlinkC`.1694/// See also `unlinkC`.
1615pub fn unlink(file_path: []const u8) UnlinkError!void {1695pub fn unlink(file_path: []const u8) UnlinkError!void {
1616 if (builtin.os.tag == .windows) {1696 if (builtin.os.tag == .wasi) {
1697 @compileError("unlink is not supported in WASI; use unlinkat instead");
1698 } else if (builtin.os.tag == .windows) {
1617 const file_path_w = try windows.sliceToPrefixedFileW(file_path);1699 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1618 return windows.DeleteFileW(file_path_w.span().ptr);1700 return windows.DeleteFileW(file_path_w.span().ptr);
1619 } else {1701 } else {
...@@ -1670,6 +1752,8 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo...@@ -1670,6 +1752,8 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo
16701752
1671pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ");1753pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ");
16721754
1755/// WASI-only. Same as `unlinkat` but targeting WASI.
1756/// See also `unlinkat`.
1673pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {1757pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
1674 const remove_dir = (flags & AT_REMOVEDIR) != 0;1758 const remove_dir = (flags & AT_REMOVEDIR) != 0;
1675 const res = if (remove_dir)1759 const res = if (remove_dir)
...@@ -1691,6 +1775,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro...@@ -1691,6 +1775,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro
1691 wasi.ENOMEM => return error.SystemResources,1775 wasi.ENOMEM => return error.SystemResources,
1692 wasi.EROFS => return error.ReadOnlyFileSystem,1776 wasi.EROFS => return error.ReadOnlyFileSystem,
1693 wasi.ENOTEMPTY => return error.DirNotEmpty,1777 wasi.ENOTEMPTY => return error.DirNotEmpty,
1778 wasi.ENOTCAPABLE => return error.AccessDenied,
16941779
1695 wasi.EINVAL => unreachable, // invalid flags, or pathname has . as last component1780 wasi.EINVAL => unreachable, // invalid flags, or pathname has . as last component
1696 wasi.EBADF => unreachable, // always a race condition1781 wasi.EBADF => unreachable, // always a race condition
...@@ -1793,6 +1878,8 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatEr...@@ -1793,6 +1878,8 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatEr
1793}1878}
17941879
1795const RenameError = error{1880const RenameError = error{
1881 /// In WASI, this error may occur when the file descriptor does
1882 /// not hold the required rights to rename a resource by path relative to it.
1796 AccessDenied,1883 AccessDenied,
1797 FileBusy,1884 FileBusy,
1798 DiskQuota,1885 DiskQuota,
...@@ -1816,7 +1903,9 @@ const RenameError = error{...@@ -1816,7 +1903,9 @@ const RenameError = error{
18161903
1817/// Change the name or location of a file.1904/// Change the name or location of a file.
1818pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {1905pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
1819 if (builtin.os.tag == .windows) {1906 if (builtin.os.tag == .wasi) {
1907 @compileError("rename is not supported in WASI; use renameat instead");
1908 } else if (builtin.os.tag == .windows) {
1820 const old_path_w = try windows.sliceToPrefixedFileW(old_path);1909 const old_path_w = try windows.sliceToPrefixedFileW(old_path);
1821 const new_path_w = try windows.sliceToPrefixedFileW(new_path);1910 const new_path_w = try windows.sliceToPrefixedFileW(new_path);
1822 return renameW(old_path_w.span().ptr, new_path_w.span().ptr);1911 return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
...@@ -1887,7 +1976,8 @@ pub fn renameat(...@@ -1887,7 +1976,8 @@ pub fn renameat(
1887 }1976 }
1888}1977}
18891978
1890/// Same as `renameat` expect only WASI.1979/// WASI-only. Same as `renameat` expect targeting WASI.
1980/// See also `renameat`.
1891pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void {1981pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void {
1892 switch (wasi.path_rename(old_dir_fd, old_path.ptr, old_path.len, new_dir_fd, new_path.ptr, new_path.len)) {1982 switch (wasi.path_rename(old_dir_fd, old_path.ptr, old_path.len, new_dir_fd, new_path.ptr, new_path.len)) {
1893 wasi.ESUCCESS => return,1983 wasi.ESUCCESS => return,
...@@ -1909,6 +1999,7 @@ pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, ne...@@ -1909,6 +1999,7 @@ pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, ne
1909 wasi.ENOTEMPTY => return error.PathAlreadyExists,1999 wasi.ENOTEMPTY => return error.PathAlreadyExists,
1910 wasi.EROFS => return error.ReadOnlyFileSystem,2000 wasi.EROFS => return error.ReadOnlyFileSystem,
1911 wasi.EXDEV => return error.RenameAcrossMountPoints,2001 wasi.EXDEV => return error.RenameAcrossMountPoints,
2002 wasi.ENOTCAPABLE => return error.AccessDenied,
1912 else => |err| return unexpectedErrno(err),2003 else => |err| return unexpectedErrno(err),
1913 }2004 }
1914}2005}
...@@ -2007,23 +2098,6 @@ pub fn renameatW(...@@ -2007,23 +2098,6 @@ pub fn renameatW(
2007 }2098 }
2008}2099}
20092100
2010pub const MakeDirError = error{
2011 AccessDenied,
2012 DiskQuota,
2013 PathAlreadyExists,
2014 SymLinkLoop,
2015 LinkQuotaExceeded,
2016 NameTooLong,
2017 FileNotFound,
2018 SystemResources,
2019 NoSpaceLeft,
2020 NotDir,
2021 ReadOnlyFileSystem,
2022 InvalidUtf8,
2023 BadPathName,
2024 NoDevice,
2025} || UnexpectedError;
2026
2027pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {2101pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
2028 if (builtin.os.tag == .windows) {2102 if (builtin.os.tag == .windows) {
2029 const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);2103 const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
...@@ -2055,6 +2129,7 @@ pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirErr...@@ -2055,6 +2129,7 @@ pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirErr
2055 wasi.ENOSPC => return error.NoSpaceLeft,2129 wasi.ENOSPC => return error.NoSpaceLeft,
2056 wasi.ENOTDIR => return error.NotDir,2130 wasi.ENOTDIR => return error.NotDir,
2057 wasi.EROFS => return error.ReadOnlyFileSystem,2131 wasi.EROFS => return error.ReadOnlyFileSystem,
2132 wasi.ENOTCAPABLE => return error.AccessDenied,
2058 else => |err| return unexpectedErrno(err),2133 else => |err| return unexpectedErrno(err),
2059 }2134 }
2060}2135}
...@@ -2089,10 +2164,31 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirErro...@@ -2089,10 +2164,31 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirErro
2089 windows.CloseHandle(sub_dir_handle);2164 windows.CloseHandle(sub_dir_handle);
2090}2165}
20912166
2167pub const MakeDirError = error{
2168 /// In WASI, this error may occur when the file descriptor does
2169 /// not hold the required rights to create a new directory relative to it.
2170 AccessDenied,
2171 DiskQuota,
2172 PathAlreadyExists,
2173 SymLinkLoop,
2174 LinkQuotaExceeded,
2175 NameTooLong,
2176 FileNotFound,
2177 SystemResources,
2178 NoSpaceLeft,
2179 NotDir,
2180 ReadOnlyFileSystem,
2181 InvalidUtf8,
2182 BadPathName,
2183 NoDevice,
2184} || UnexpectedError;
2185
2092/// Create a directory.2186/// Create a directory.
2093/// `mode` is ignored on Windows.2187/// `mode` is ignored on Windows.
2094pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {2188pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
2095 if (builtin.os.tag == .windows) {2189 if (builtin.os.tag == .wasi) {
2190 @compileError("mkdir is not supported in WASI; use mkdirat instead");
2191 } else if (builtin.os.tag == .windows) {
2096 const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null);2192 const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null);
2097 windows.CloseHandle(sub_dir_handle);2193 windows.CloseHandle(sub_dir_handle);
2098 return;2194 return;
...@@ -2145,7 +2241,9 @@ pub const DeleteDirError = error{...@@ -2145,7 +2241,9 @@ pub const DeleteDirError = error{
21452241
2146/// Deletes an empty directory.2242/// Deletes an empty directory.
2147pub fn rmdir(dir_path: []const u8) DeleteDirError!void {2243pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
2148 if (builtin.os.tag == .windows) {2244 if (builtin.os.tag == .wasi) {
2245 @compileError("rmdir is not supported in WASI; use unlinkat instead");
2246 } else if (builtin.os.tag == .windows) {
2149 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);2247 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
2150 return windows.RemoveDirectoryW(dir_path_w.span().ptr);2248 return windows.RemoveDirectoryW(dir_path_w.span().ptr);
2151 } else {2249 } else {
...@@ -2194,7 +2292,9 @@ pub const ChangeCurDirError = error{...@@ -2194,7 +2292,9 @@ pub const ChangeCurDirError = error{
2194/// Changes the current working directory of the calling process.2292/// Changes the current working directory of the calling process.
2195/// `dir_path` is recommended to be a UTF-8 encoded string.2293/// `dir_path` is recommended to be a UTF-8 encoded string.
2196pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {2294pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
2197 if (builtin.os.tag == .windows) {2295 if (builtin.os.tag == .wasi) {
2296 @compileError("chdir is not supported in WASI");
2297 } else if (builtin.os.tag == .windows) {
2198 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);2298 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
2199 @compileError("TODO implement chdir for Windows");2299 @compileError("TODO implement chdir for Windows");
2200 } else {2300 } else {
...@@ -2246,6 +2346,8 @@ pub fn fchdir(dirfd: fd_t) FchdirError!void {...@@ -2246,6 +2346,8 @@ pub fn fchdir(dirfd: fd_t) FchdirError!void {
2246}2346}
22472347
2248pub const ReadLinkError = error{2348pub const ReadLinkError = error{
2349 /// In WASI, this error may occur when the file descriptor does
2350 /// not hold the required rights to read value of a symbolic link relative to it.
2249 AccessDenied,2351 AccessDenied,
2250 FileSystem,2352 FileSystem,
2251 SymLinkLoop,2353 SymLinkLoop,
...@@ -2258,9 +2360,11 @@ pub const ReadLinkError = error{...@@ -2258,9 +2360,11 @@ pub const ReadLinkError = error{
2258/// Read value of a symbolic link.2360/// Read value of a symbolic link.
2259/// The return value is a slice of `out_buffer` from index 0.2361/// The return value is a slice of `out_buffer` from index 0.
2260pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {2362pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
2261 if (builtin.os.tag == .windows) {2363 if (builtin.os.tag == .wasi) {
2364 @compileError("readlink is not supported in WASI; use readlinkat instead");
2365 } else if (builtin.os.tag == .windows) {
2262 const file_path_w = try windows.sliceToPrefixedFileW(file_path);2366 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
2263 @compileError("TODO implement readlink for Windows");2367 return readlinkW(file_path_w.span().ptr, out_buffer);
2264 } else {2368 } else {
2265 const file_path_c = try toPosixPath(file_path);2369 const file_path_c = try toPosixPath(file_path);
2266 return readlinkZ(&file_path_c, out_buffer);2370 return readlinkZ(&file_path_c, out_buffer);
...@@ -2269,11 +2373,17 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {...@@ -2269,11 +2373,17 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
22692373
2270pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");2374pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");
22712375
2376/// Windows-only. Same as `readlink` expecte `file_path` is null-terminated, WTF16 encoded.
2377/// Seel also `readlinkZ`.
2378pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
2379 @compileError("TODO implement readlink for Windows");
2380}
2381
2272/// Same as `readlink` except `file_path` is null-terminated.2382/// Same as `readlink` except `file_path` is null-terminated.
2273pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {2383pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
2274 if (builtin.os.tag == .windows) {2384 if (builtin.os.tag == .windows) {
2275 const file_path_w = try windows.cStrToPrefixedFileW(file_path);2385 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
2276 @compileError("TODO implement readlink for Windows");2386 return readlinkW(file_path_w.span().ptr, out_buffer);
2277 }2387 }
2278 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);2388 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
2279 switch (errno(rc)) {2389 switch (errno(rc)) {
...@@ -2291,12 +2401,55 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8...@@ -2291,12 +2401,55 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
2291 }2401 }
2292}2402}
22932403
2404/// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle.
2405/// The return value is a slice of `out_buffer` from index 0.
2406/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
2407pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
2408 if (builtin.os.tag == .wasi) {
2409 return readlinkatWasi(dirfd, file_path, out_buffer);
2410 }
2411 if (builtin.os.tag == .windows) {
2412 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
2413 return readlinkatW(dirfd, file_path.span().ptr, out_buffer);
2414 }
2415 const file_path_c = try toPosixPath(file_path);
2416 return readlinkatZ(dirfd, &file_path_c, out_buffer);
2417}
2418
2294pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ");2419pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ");
22952420
2421/// WASI-only. Same as `readlinkat` but targets WASI.
2422/// See also `readlinkat`.
2423pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
2424 var bufused: usize = undefined;
2425 switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) {
2426 wasi.ESUCCESS => return out_buffer[0..bufused],
2427 wasi.EACCES => return error.AccessDenied,
2428 wasi.EFAULT => unreachable,
2429 wasi.EINVAL => unreachable,
2430 wasi.EIO => return error.FileSystem,
2431 wasi.ELOOP => return error.SymLinkLoop,
2432 wasi.ENAMETOOLONG => return error.NameTooLong,
2433 wasi.ENOENT => return error.FileNotFound,
2434 wasi.ENOMEM => return error.SystemResources,
2435 wasi.ENOTDIR => return error.NotDir,
2436 wasi.ENOTCAPABLE => return error.AccessDenied,
2437 else => |err| return unexpectedErrno(err),
2438 }
2439}
2440
2441/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
2442/// See also `readlinkat`.
2443pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
2444 @compileError("TODO implement on Windows");
2445}
2446
2447/// Same as `readlinkat` except `file_path` is null-terminated.
2448/// See also `readlinkat`.
2296pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {2449pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
2297 if (builtin.os.tag == .windows) {2450 if (builtin.os.tag == .windows) {
2298 const file_path_w = try windows.cStrToPrefixedFileW(file_path);2451 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
2299 @compileError("TODO implement readlink for Windows");2452 return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer);
2300 }2453 }
2301 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);2454 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
2302 switch (errno(rc)) {2455 switch (errno(rc)) {
...@@ -2958,9 +3111,13 @@ pub fn waitpid(pid: i32, flags: u32) u32 {...@@ -2958,9 +3111,13 @@ pub fn waitpid(pid: i32, flags: u32) u32 {
29583111
2959pub const FStatError = error{3112pub const FStatError = error{
2960 SystemResources,3113 SystemResources,
3114
3115 /// In WASI, this error may occur when the file descriptor does
3116 /// not hold the required rights to get its filestat information.
2961 AccessDenied,3117 AccessDenied,
2962} || UnexpectedError;3118} || UnexpectedError;
29633119
3120/// Return information about a file descriptor.
2964pub fn fstat(fd: fd_t) FStatError!Stat {3121pub fn fstat(fd: fd_t) FStatError!Stat {
2965 if (builtin.os.tag == .wasi) {3122 if (builtin.os.tag == .wasi) {
2966 var stat: wasi.filestat_t = undefined;3123 var stat: wasi.filestat_t = undefined;
...@@ -2970,9 +3127,13 @@ pub fn fstat(fd: fd_t) FStatError!Stat {...@@ -2970,9 +3127,13 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
2970 wasi.EBADF => unreachable, // Always a race condition.3127 wasi.EBADF => unreachable, // Always a race condition.
2971 wasi.ENOMEM => return error.SystemResources,3128 wasi.ENOMEM => return error.SystemResources,
2972 wasi.EACCES => return error.AccessDenied,3129 wasi.EACCES => return error.AccessDenied,
3130 wasi.ENOTCAPABLE => return error.AccessDenied,
2973 else => |err| return unexpectedErrno(err),3131 else => |err| return unexpectedErrno(err),
2974 }3132 }
2975 }3133 }
3134 if (builtin.os.tag == .windows) {
3135 @compileError("fstat is not yet implemented on Windows");
3136 }
29763137
2977 var stat: Stat = undefined;3138 var stat: Stat = undefined;
2978 switch (errno(system.fstat(fd, &stat))) {3139 switch (errno(system.fstat(fd, &stat))) {
...@@ -2987,13 +3148,43 @@ pub fn fstat(fd: fd_t) FStatError!Stat {...@@ -2987,13 +3148,43 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
29873148
2988pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound };3149pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound };
29893150
3151/// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
3152/// which is relative to `dirfd` handle.
3153/// See also `fstatatZ` and `fstatatWasi`.
2990pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {3154pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
2991 const pathname_c = try toPosixPath(pathname);3155 if (builtin.os.tag == .wasi) {
2992 return fstatatZ(dirfd, &pathname_c, flags);3156 return fstatatWasi(dirfd, pathname, flags);
3157 } else if (builtin.os.tag == .windows) {
3158 @compileError("fstatat is not yet implemented on Windows");
3159 } else {
3160 const pathname_c = try toPosixPath(pathname);
3161 return fstatatZ(dirfd, &pathname_c, flags);
3162 }
2993}3163}
29943164
2995pub const fstatatC = @compileError("deprecated: renamed to fstatatZ");3165pub const fstatatC = @compileError("deprecated: renamed to fstatatZ");
29963166
3167/// WASI-only. Same as `fstatat` but targeting WASI.
3168/// See also `fstatat`.
3169pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
3170 var stat: wasi.filestat_t = undefined;
3171 switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) {
3172 wasi.ESUCCESS => return Stat.fromFilestat(stat),
3173 wasi.EINVAL => unreachable,
3174 wasi.EBADF => unreachable, // Always a race condition.
3175 wasi.ENOMEM => return error.SystemResources,
3176 wasi.EACCES => return error.AccessDenied,
3177 wasi.EFAULT => unreachable,
3178 wasi.ENAMETOOLONG => return error.NameTooLong,
3179 wasi.ENOENT => return error.FileNotFound,
3180 wasi.ENOTDIR => return error.FileNotFound,
3181 wasi.ENOTCAPABLE => return error.AccessDenied,
3182 else => |err| return unexpectedErrno(err),
3183 }
3184}
3185
3186/// Same as `fstatat` but `pathname` is null-terminated.
3187/// See also `fstatat`.
2997pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {3188pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
2998 var stat: Stat = undefined;3189 var stat: Stat = undefined;
2999 switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) {3190 switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) {
...@@ -3493,7 +3684,13 @@ pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {...@@ -3493,7 +3684,13 @@ pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
3493 }3684 }
3494}3685}
34953686
3496pub const SeekError = error{Unseekable} || UnexpectedError;3687pub const SeekError = error{
3688 Unseekable,
3689
3690 /// In WASI, this error may occur when the file descriptor does
3691 /// not hold the required rights to seek on it.
3692 AccessDenied,
3693} || UnexpectedError;
34973694
3498/// Repositions read/write file offset relative to the beginning.3695/// Repositions read/write file offset relative to the beginning.
3499pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {3696pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
...@@ -3521,6 +3718,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {...@@ -3521,6 +3718,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
3521 wasi.EOVERFLOW => return error.Unseekable,3718 wasi.EOVERFLOW => return error.Unseekable,
3522 wasi.ESPIPE => return error.Unseekable,3719 wasi.ESPIPE => return error.Unseekable,
3523 wasi.ENXIO => return error.Unseekable,3720 wasi.ENXIO => return error.Unseekable,
3721 wasi.ENOTCAPABLE => return error.AccessDenied,
3524 else => |err| return unexpectedErrno(err),3722 else => |err| return unexpectedErrno(err),
3525 }3723 }
3526 }3724 }
...@@ -3562,6 +3760,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {...@@ -3562,6 +3760,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
3562 wasi.EOVERFLOW => return error.Unseekable,3760 wasi.EOVERFLOW => return error.Unseekable,
3563 wasi.ESPIPE => return error.Unseekable,3761 wasi.ESPIPE => return error.Unseekable,
3564 wasi.ENXIO => return error.Unseekable,3762 wasi.ENXIO => return error.Unseekable,
3763 wasi.ENOTCAPABLE => return error.AccessDenied,
3565 else => |err| return unexpectedErrno(err),3764 else => |err| return unexpectedErrno(err),
3566 }3765 }
3567 }3766 }
...@@ -3602,6 +3801,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {...@@ -3602,6 +3801,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
3602 wasi.EOVERFLOW => return error.Unseekable,3801 wasi.EOVERFLOW => return error.Unseekable,
3603 wasi.ESPIPE => return error.Unseekable,3802 wasi.ESPIPE => return error.Unseekable,
3604 wasi.ENXIO => return error.Unseekable,3803 wasi.ENXIO => return error.Unseekable,
3804 wasi.ENOTCAPABLE => return error.AccessDenied,
3605 else => |err| return unexpectedErrno(err),3805 else => |err| return unexpectedErrno(err),
3606 }3806 }
3607 }3807 }
...@@ -3642,6 +3842,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {...@@ -3642,6 +3842,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
3642 wasi.EOVERFLOW => return error.Unseekable,3842 wasi.EOVERFLOW => return error.Unseekable,
3643 wasi.ESPIPE => return error.Unseekable,3843 wasi.ESPIPE => return error.Unseekable,
3644 wasi.ENXIO => return error.Unseekable,3844 wasi.ENXIO => return error.Unseekable,
3845 wasi.ENOTCAPABLE => return error.AccessDenied,
3645 else => |err| return unexpectedErrno(err),3846 else => |err| return unexpectedErrno(err),
3646 }3847 }
3647 }3848 }
...@@ -3867,7 +4068,7 @@ pub fn nanosleep(seconds: u64, nanoseconds: u64) void {...@@ -3867,7 +4068,7 @@ pub fn nanosleep(seconds: u64, nanoseconds: u64) void {
3867}4068}
38684069
3869pub fn dl_iterate_phdr(4070pub fn dl_iterate_phdr(
3870 context: var,4071 context: anytype,
3871 comptime Error: type,4072 comptime Error: type,
3872 comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void,4073 comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void,
3873) Error!void {4074) Error!void {
lib/std/os/test.zig+27-143
...@@ -18,135 +18,49 @@ const AtomicOrder = builtin.AtomicOrder;...@@ -18,135 +18,49 @@ const AtomicOrder = builtin.AtomicOrder;
18const tmpDir = std.testing.tmpDir;18const tmpDir = std.testing.tmpDir;
19const Dir = std.fs.Dir;19const Dir = std.fs.Dir;
2020
21test "makePath, put some files in it, deleteTree" {21test "fstatat" {
22 var tmp = tmpDir(.{});22 // enable when `fstat` and `fstatat` are implemented on Windows
23 defer tmp.cleanup();23 if (builtin.os.tag == .windows) return error.SkipZigTest;
24
25 try tmp.dir.makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
26 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
27 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
28 try tmp.dir.deleteTree("os_test_tmp");
29 if (tmp.dir.openDir("os_test_tmp", .{})) |dir| {
30 @panic("expected error");
31 } else |err| {
32 expect(err == error.FileNotFound);
33 }
34}
35
36test "access file" {
37 if (builtin.os.tag == .wasi) return error.SkipZigTest;
38
39 var tmp = tmpDir(.{});
40 defer tmp.cleanup();
4124
42 try tmp.dir.makePath("os_test_tmp");
43 if (tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| {
44 @panic("expected error");
45 } else |err| {
46 expect(err == error.FileNotFound);
47 }
48
49 try tmp.dir.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
50 try tmp.dir.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{});
51 try tmp.dir.deleteTree("os_test_tmp");
52}
53
54fn testThreadIdFn(thread_id: *Thread.Id) void {
55 thread_id.* = Thread.getCurrentId();
56}
57
58test "sendfile" {
59 var tmp = tmpDir(.{});25 var tmp = tmpDir(.{});
60 defer tmp.cleanup();26 defer tmp.cleanup();
6127
62 try tmp.dir.makePath("os_test_tmp");28 // create dummy file
63 defer tmp.dir.deleteTree("os_test_tmp") catch {};29 const contents = "nonsense";
6430 try tmp.dir.writeFile("file.txt", contents);
65 var dir = try tmp.dir.openDir("os_test_tmp", .{});
66 defer dir.close();
67
68 const line1 = "line1\n";
69 const line2 = "second line\n";
70 var vecs = [_]os.iovec_const{
71 .{
72 .iov_base = line1,
73 .iov_len = line1.len,
74 },
75 .{
76 .iov_base = line2,
77 .iov_len = line2.len,
78 },
79 };
8031
81 var src_file = try dir.createFile("sendfile1.txt", .{ .read = true });32 // fetch file's info on the opened fd directly
82 defer src_file.close();33 const file = try tmp.dir.openFile("file.txt", .{});
8334 const stat = try os.fstat(file.handle);
84 try src_file.writevAll(&vecs);35 defer file.close();
85
86 var dest_file = try dir.createFile("sendfile2.txt", .{ .read = true });
87 defer dest_file.close();
88
89 const header1 = "header1\n";
90 const header2 = "second header\n";
91 const trailer1 = "trailer1\n";
92 const trailer2 = "second trailer\n";
93 var hdtr = [_]os.iovec_const{
94 .{
95 .iov_base = header1,
96 .iov_len = header1.len,
97 },
98 .{
99 .iov_base = header2,
100 .iov_len = header2.len,
101 },
102 .{
103 .iov_base = trailer1,
104 .iov_len = trailer1.len,
105 },
106 .{
107 .iov_base = trailer2,
108 .iov_len = trailer2.len,
109 },
110 };
11136
112 var written_buf: [100]u8 = undefined;37 // now repeat but using `fstatat` instead
113 try dest_file.writeFileAll(src_file, .{38 const flags = if (builtin.os.tag == .wasi) 0x0 else os.AT_SYMLINK_NOFOLLOW;
114 .in_offset = 1,39 const statat = try os.fstatat(tmp.dir.fd, "file.txt", flags);
115 .in_len = 10,40 expectEqual(stat, statat);
116 .headers_and_trailers = &hdtr,
117 .header_count = 2,
118 });
119 const amt = try dest_file.preadAll(&written_buf, 0);
120 expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
121}41}
12242
123test "fs.copyFile" {43test "readlinkat" {
124 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";44 // enable when `readlinkat` and `symlinkat` are implemented on Windows
125 const src_file = "tmp_test_copy_file.txt";45 if (builtin.os.tag == .windows) return error.SkipZigTest;
126 const dest_file = "tmp_test_copy_file2.txt";
127 const dest_file2 = "tmp_test_copy_file3.txt";
12846
129 var tmp = tmpDir(.{});47 var tmp = tmpDir(.{});
130 defer tmp.cleanup();48 defer tmp.cleanup();
13149
132 try tmp.dir.writeFile(src_file, data);50 // create file
133 defer tmp.dir.deleteFile(src_file) catch {};51 try tmp.dir.writeFile("file.txt", "nonsense");
13452
135 try tmp.dir.copyFile(src_file, tmp.dir, dest_file, .{});53 // create a symbolic link
136 defer tmp.dir.deleteFile(dest_file) catch {};54 try os.symlinkat("file.txt", tmp.dir.fd, "link");
13755
138 try tmp.dir.copyFile(src_file, tmp.dir, dest_file2, .{ .override_mode = File.default_mode });56 // read the link
139 defer tmp.dir.deleteFile(dest_file2) catch {};57 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
14058 const read_link = try os.readlinkat(tmp.dir.fd, "link", buffer[0..]);
141 try expectFileContents(tmp.dir, dest_file, data);59 expect(mem.eql(u8, "file.txt", read_link));
142 try expectFileContents(tmp.dir, dest_file2, data);
143}60}
14461
145fn expectFileContents(dir: Dir, file_path: []const u8, data: []const u8) !void {62fn testThreadIdFn(thread_id: *Thread.Id) void {
146 const contents = try dir.readFileAlloc(testing.allocator, file_path, 1000);63 thread_id.* = Thread.getCurrentId();
147 defer testing.allocator.free(contents);
148
149 testing.expectEqualSlices(u8, data, contents);
150}64}
15165
152test "std.Thread.getCurrentId" {66test "std.Thread.getCurrentId" {
...@@ -201,29 +115,6 @@ test "cpu count" {...@@ -201,29 +115,6 @@ test "cpu count" {
201 expect(cpu_count >= 1);115 expect(cpu_count >= 1);
202}116}
203117
204test "AtomicFile" {
205 const test_out_file = "tmp_atomic_file_test_dest.txt";
206 const test_content =
207 \\ hello!
208 \\ this is a test file
209 ;
210
211 var tmp = tmpDir(.{});
212 defer tmp.cleanup();
213
214 {
215 var af = try tmp.dir.atomicFile(test_out_file, .{});
216 defer af.deinit();
217 try af.file.writeAll(test_content);
218 try af.finish();
219 }
220 const content = try tmp.dir.readFileAlloc(testing.allocator, test_out_file, 9999);
221 defer testing.allocator.free(content);
222 expect(mem.eql(u8, content, test_content));
223
224 try tmp.dir.deleteFile(test_out_file);
225}
226
227test "thread local storage" {118test "thread local storage" {
228 if (builtin.single_threaded) return error.SkipZigTest;119 if (builtin.single_threaded) return error.SkipZigTest;
229 const thread1 = try Thread.spawn({}, testTls);120 const thread1 = try Thread.spawn({}, testTls);
...@@ -258,13 +149,6 @@ test "getcwd" {...@@ -258,13 +149,6 @@ test "getcwd" {
258 _ = os.getcwd(&buf) catch undefined;149 _ = os.getcwd(&buf) catch undefined;
259}150}
260151
261test "realpath" {
262 if (builtin.os.tag == .wasi) return error.SkipZigTest;
263
264 var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
265 testing.expectError(error.FileNotFound, fs.realpath("definitely_bogus_does_not_exist1234", &buf));
266}
267
268test "sigaltstack" {152test "sigaltstack" {
269 if (builtin.os.tag == .windows or builtin.os.tag == .wasi) return error.SkipZigTest;153 if (builtin.os.tag == .windows or builtin.os.tag == .wasi) return error.SkipZigTest;
270154
lib/std/os/uefi.zig+1-1
...@@ -28,7 +28,7 @@ pub const Guid = extern struct {...@@ -28,7 +28,7 @@ pub const Guid = extern struct {
28 self: @This(),28 self: @This(),
29 comptime f: []const u8,29 comptime f: []const u8,
30 options: std.fmt.FormatOptions,30 options: std.fmt.FormatOptions,
31 out_stream: var,31 out_stream: anytype,
32 ) Errors!void {32 ) Errors!void {
33 if (f.len == 0) {33 if (f.len == 0) {
34 return std.fmt.format(out_stream, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{34 return std.fmt.format(out_stream, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{
lib/std/os/windows.zig+10-1
...@@ -901,7 +901,13 @@ pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA {...@@ -901,7 +901,13 @@ pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA {
901 var wsadata: ws2_32.WSADATA = undefined;901 var wsadata: ws2_32.WSADATA = undefined;
902 return switch (ws2_32.WSAStartup((@as(WORD, minorVersion) << 8) | majorVersion, &wsadata)) {902 return switch (ws2_32.WSAStartup((@as(WORD, minorVersion) << 8) | majorVersion, &wsadata)) {
903 0 => wsadata,903 0 => wsadata,
904 else => |err| unexpectedWSAError(@intToEnum(ws2_32.WinsockError, @intCast(u16, err))),904 else => |err_int| switch (@intToEnum(ws2_32.WinsockError, @intCast(u16, err_int))) {
905 .WSASYSNOTREADY => return error.SystemNotAvailable,
906 .WSAVERNOTSUPPORTED => return error.VersionNotSupported,
907 .WSAEINPROGRESS => return error.BlockingOperationInProgress,
908 .WSAEPROCLIM => return error.SystemResources,
909 else => |err| return unexpectedWSAError(err),
910 },
905 };911 };
906}912}
907913
...@@ -909,6 +915,9 @@ pub fn WSACleanup() !void {...@@ -909,6 +915,9 @@ pub fn WSACleanup() !void {
909 return switch (ws2_32.WSACleanup()) {915 return switch (ws2_32.WSACleanup()) {
910 0 => {},916 0 => {},
911 ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {917 ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
918 .WSANOTINITIALISED => return error.NotInitialized,
919 .WSAENETDOWN => return error.NetworkNotAvailable,
920 .WSAEINPROGRESS => return error.BlockingOperationInProgress,
912 else => |err| return unexpectedWSAError(err),921 else => |err| return unexpectedWSAError(err),
913 },922 },
914 else => unreachable,923 else => unreachable,
lib/std/os/windows/bits.zig+1
...@@ -593,6 +593,7 @@ pub const FILE_CURRENT = 1;...@@ -593,6 +593,7 @@ pub const FILE_CURRENT = 1;
593pub const FILE_END = 2;593pub const FILE_END = 2;
594594
595pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;595pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
596pub const HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010;
596pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;597pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
597pub const HEAP_NO_SERIALIZE = 0x00000001;598pub const HEAP_NO_SERIALIZE = 0x00000001;
598599
lib/std/os/windows/ws2_32.zig+9-9
...@@ -163,16 +163,16 @@ pub const IPPROTO_UDP = 17;...@@ -163,16 +163,16 @@ pub const IPPROTO_UDP = 17;
163pub const IPPROTO_ICMPV6 = 58;163pub const IPPROTO_ICMPV6 = 58;
164pub const IPPROTO_RM = 113;164pub const IPPROTO_RM = 113;
165165
166pub const AI_PASSIVE = 0x00001;166pub const AI_PASSIVE = 0x00001;
167pub const AI_CANONNAME = 0x00002;167pub const AI_CANONNAME = 0x00002;
168pub const AI_NUMERICHOST = 0x00004;168pub const AI_NUMERICHOST = 0x00004;
169pub const AI_NUMERICSERV = 0x00008;169pub const AI_NUMERICSERV = 0x00008;
170pub const AI_ADDRCONFIG = 0x00400;170pub const AI_ADDRCONFIG = 0x00400;
171pub const AI_V4MAPPED = 0x00800;171pub const AI_V4MAPPED = 0x00800;
172pub const AI_NON_AUTHORITATIVE = 0x04000;172pub const AI_NON_AUTHORITATIVE = 0x04000;
173pub const AI_SECURE = 0x08000;173pub const AI_SECURE = 0x08000;
174pub const AI_RETURN_PREFERRED_NAMES = 0x10000;174pub const AI_RETURN_PREFERRED_NAMES = 0x10000;
175pub const AI_DISABLE_IDN_ENCODING = 0x80000;175pub const AI_DISABLE_IDN_ENCODING = 0x80000;
176176
177pub const FIONBIO = -2147195266;177pub const FIONBIO = -2147195266;
178178
lib/std/pdb.zig+1-1
...@@ -469,7 +469,7 @@ pub const Pdb = struct {...@@ -469,7 +469,7 @@ pub const Pdb = struct {
469469
470 msf: Msf,470 msf: Msf,
471471
472 pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void {472 pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []const u8) !void {
473 self.in_file = try fs.cwd().openFile(file_name, .{ .intended_io_mode = .blocking });473 self.in_file = try fs.cwd().openFile(file_name, .{ .intended_io_mode = .blocking });
474 self.allocator = coff_ptr.allocator;474 self.allocator = coff_ptr.allocator;
475 self.coff = coff_ptr;475 self.coff = coff_ptr;
lib/std/priority_queue.zig+1-1
...@@ -333,7 +333,7 @@ test "std.PriorityQueue: addSlice" {...@@ -333,7 +333,7 @@ test "std.PriorityQueue: addSlice" {
333333
334test "std.PriorityQueue: fromOwnedSlice" {334test "std.PriorityQueue: fromOwnedSlice" {
335 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };335 const items = [_]u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 };
336 const heap_items = try std.mem.dupe(testing.allocator, u32, items[0..]);336 const heap_items = try testing.allocator.dupe(u32, items[0..]);
337 var queue = PQ.fromOwnedSlice(testing.allocator, lessThan, heap_items[0..]);337 var queue = PQ.fromOwnedSlice(testing.allocator, lessThan, heap_items[0..]);
338 defer queue.deinit();338 defer queue.deinit();
339339
lib/std/process.zig+12-39
...@@ -30,7 +30,7 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {...@@ -30,7 +30,7 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
30 var current_buf: []u8 = &stack_buf;30 var current_buf: []u8 = &stack_buf;
31 while (true) {31 while (true) {
32 if (os.getcwd(current_buf)) |slice| {32 if (os.getcwd(current_buf)) |slice| {
33 return mem.dupe(allocator, u8, slice);33 return allocator.dupe(u8, slice);
34 } else |err| switch (err) {34 } else |err| switch (err) {
35 error.NameTooLong => {35 error.NameTooLong => {
36 // The path is too long to fit in stack_buf. Allocate geometrically36 // The path is too long to fit in stack_buf. Allocate geometrically
...@@ -169,7 +169,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned...@@ -169,7 +169,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned
169 };169 };
170 } else {170 } else {
171 const result = os.getenv(key) orelse return error.EnvironmentVariableNotFound;171 const result = os.getenv(key) orelse return error.EnvironmentVariableNotFound;
172 return mem.dupe(allocator, u8, result);172 return allocator.dupe(u8, result);
173 }173 }
174}174}
175175
...@@ -281,9 +281,6 @@ pub const ArgIteratorWasi = struct {...@@ -281,9 +281,6 @@ pub const ArgIteratorWasi = struct {
281pub const ArgIteratorWindows = struct {281pub const ArgIteratorWindows = struct {
282 index: usize,282 index: usize,
283 cmd_line: [*]const u8,283 cmd_line: [*]const u8,
284 in_quote: bool,
285 quote_count: usize,
286 seen_quote_count: usize,
287284
288 pub const NextError = error{OutOfMemory};285 pub const NextError = error{OutOfMemory};
289286
...@@ -295,9 +292,6 @@ pub const ArgIteratorWindows = struct {...@@ -295,9 +292,6 @@ pub const ArgIteratorWindows = struct {
295 return ArgIteratorWindows{292 return ArgIteratorWindows{
296 .index = 0,293 .index = 0,
297 .cmd_line = cmd_line,294 .cmd_line = cmd_line,
298 .in_quote = false,
299 .quote_count = countQuotes(cmd_line),
300 .seen_quote_count = 0,
301 };295 };
302 }296 }
303297
...@@ -328,6 +322,7 @@ pub const ArgIteratorWindows = struct {...@@ -328,6 +322,7 @@ pub const ArgIteratorWindows = struct {
328 }322 }
329323
330 var backslash_count: usize = 0;324 var backslash_count: usize = 0;
325 var in_quote = false;
331 while (true) : (self.index += 1) {326 while (true) : (self.index += 1) {
332 const byte = self.cmd_line[self.index];327 const byte = self.cmd_line[self.index];
333 switch (byte) {328 switch (byte) {
...@@ -335,14 +330,14 @@ pub const ArgIteratorWindows = struct {...@@ -335,14 +330,14 @@ pub const ArgIteratorWindows = struct {
335 '"' => {330 '"' => {
336 const quote_is_real = backslash_count % 2 == 0;331 const quote_is_real = backslash_count % 2 == 0;
337 if (quote_is_real) {332 if (quote_is_real) {
338 self.seen_quote_count += 1;333 in_quote = !in_quote;
339 }334 }
340 },335 },
341 '\\' => {336 '\\' => {
342 backslash_count += 1;337 backslash_count += 1;
343 },338 },
344 ' ', '\t' => {339 ' ', '\t' => {
345 if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {340 if (!in_quote) {
346 return true;341 return true;
347 }342 }
348 backslash_count = 0;343 backslash_count = 0;
...@@ -360,6 +355,7 @@ pub const ArgIteratorWindows = struct {...@@ -360,6 +355,7 @@ pub const ArgIteratorWindows = struct {
360 defer buf.deinit();355 defer buf.deinit();
361356
362 var backslash_count: usize = 0;357 var backslash_count: usize = 0;
358 var in_quote = false;
363 while (true) : (self.index += 1) {359 while (true) : (self.index += 1) {
364 const byte = self.cmd_line[self.index];360 const byte = self.cmd_line[self.index];
365 switch (byte) {361 switch (byte) {
...@@ -370,10 +366,7 @@ pub const ArgIteratorWindows = struct {...@@ -370,10 +366,7 @@ pub const ArgIteratorWindows = struct {
370 backslash_count = 0;366 backslash_count = 0;
371367
372 if (quote_is_real) {368 if (quote_is_real) {
373 self.seen_quote_count += 1;369 in_quote = !in_quote;
374 if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) {
375 try buf.append('"');
376 }
377 } else {370 } else {
378 try buf.append('"');371 try buf.append('"');
379 }372 }
...@@ -384,7 +377,7 @@ pub const ArgIteratorWindows = struct {...@@ -384,7 +377,7 @@ pub const ArgIteratorWindows = struct {
384 ' ', '\t' => {377 ' ', '\t' => {
385 try self.emitBackslashes(&buf, backslash_count);378 try self.emitBackslashes(&buf, backslash_count);
386 backslash_count = 0;379 backslash_count = 0;
387 if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {380 if (in_quote) {
388 try buf.append(byte);381 try buf.append(byte);
389 } else {382 } else {
390 return buf.toOwnedSlice();383 return buf.toOwnedSlice();
...@@ -405,26 +398,6 @@ pub const ArgIteratorWindows = struct {...@@ -405,26 +398,6 @@ pub const ArgIteratorWindows = struct {
405 try buf.append('\\');398 try buf.append('\\');
406 }399 }
407 }400 }
408
409 fn countQuotes(cmd_line: [*]const u8) usize {
410 var result: usize = 0;
411 var backslash_count: usize = 0;
412 var index: usize = 0;
413 while (true) : (index += 1) {
414 const byte = cmd_line[index];
415 switch (byte) {
416 0 => return result,
417 '\\' => backslash_count += 1,
418 '"' => {
419 result += 1 - (backslash_count % 2);
420 backslash_count = 0;
421 },
422 else => {
423 backslash_count = 0;
424 },
425 }
426 }
427 }
428};401};
429402
430pub const ArgIterator = struct {403pub const ArgIterator = struct {
...@@ -463,7 +436,7 @@ pub const ArgIterator = struct {...@@ -463,7 +436,7 @@ pub const ArgIterator = struct {
463 if (builtin.os.tag == .windows) {436 if (builtin.os.tag == .windows) {
464 return self.inner.next(allocator);437 return self.inner.next(allocator);
465 } else {438 } else {
466 return mem.dupe(allocator, u8, self.inner.next() orelse return null);439 return allocator.dupe(u8, self.inner.next() orelse return null);
467 }440 }
468 }441 }
469442
...@@ -578,7 +551,7 @@ test "windows arg parsing" {...@@ -578,7 +551,7 @@ test "windows arg parsing" {
578 testWindowsCmdLine("a\\\\\\b d\"e f\"g h", &[_][]const u8{ "a\\\\\\b", "de fg", "h" });551 testWindowsCmdLine("a\\\\\\b d\"e f\"g h", &[_][]const u8{ "a\\\\\\b", "de fg", "h" });
579 testWindowsCmdLine("a\\\\\\\"b c d", &[_][]const u8{ "a\\\"b", "c", "d" });552 testWindowsCmdLine("a\\\\\\\"b c d", &[_][]const u8{ "a\\\"b", "c", "d" });
580 testWindowsCmdLine("a\\\\\\\\\"b c\" d e", &[_][]const u8{ "a\\\\b c", "d", "e" });553 testWindowsCmdLine("a\\\\\\\\\"b c\" d e", &[_][]const u8{ "a\\\\b c", "d", "e" });
581 testWindowsCmdLine("a b\tc \"d f", &[_][]const u8{ "a", "b", "c", "\"d", "f" });554 testWindowsCmdLine("a b\tc \"d f", &[_][]const u8{ "a", "b", "c", "d f" });
582555
583 testWindowsCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &[_][]const u8{556 testWindowsCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &[_][]const u8{
584 ".\\..\\zig-cache\\build",557 ".\\..\\zig-cache\\build",
...@@ -745,7 +718,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]...@@ -745,7 +718,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]
745 fn callback(info: *os.dl_phdr_info, size: usize, list: *List) !void {718 fn callback(info: *os.dl_phdr_info, size: usize, list: *List) !void {
746 const name = info.dlpi_name orelse return;719 const name = info.dlpi_name orelse return;
747 if (name[0] == '/') {720 if (name[0] == '/') {
748 const item = try mem.dupeZ(list.allocator, u8, mem.spanZ(name));721 const item = try list.allocator.dupeZ(u8, mem.spanZ(name));
749 errdefer list.allocator.free(item);722 errdefer list.allocator.free(item);
750 try list.append(item);723 try list.append(item);
751 }724 }
...@@ -766,7 +739,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]...@@ -766,7 +739,7 @@ pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]
766 var i: u32 = 0;739 var i: u32 = 0;
767 while (i < img_count) : (i += 1) {740 while (i < img_count) : (i += 1) {
768 const name = std.c._dyld_get_image_name(i);741 const name = std.c._dyld_get_image_name(i);
769 const item = try mem.dupeZ(allocator, u8, mem.spanZ(name));742 const item = try allocator.dupeZ(u8, mem.spanZ(name));
770 errdefer allocator.free(item);743 errdefer allocator.free(item);
771 try paths.append(item);744 try paths.append(item);
772 }745 }
lib/std/progress.zig+2-2
...@@ -224,7 +224,7 @@ pub const Progress = struct {...@@ -224,7 +224,7 @@ pub const Progress = struct {
224 self.prev_refresh_timestamp = self.timer.read();224 self.prev_refresh_timestamp = self.timer.read();
225 }225 }
226226
227 pub fn log(self: *Progress, comptime format: []const u8, args: var) void {227 pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
228 const file = self.terminal orelse return;228 const file = self.terminal orelse return;
229 self.refresh();229 self.refresh();
230 file.outStream().print(format, args) catch {230 file.outStream().print(format, args) catch {
...@@ -234,7 +234,7 @@ pub const Progress = struct {...@@ -234,7 +234,7 @@ pub const Progress = struct {
234 self.columns_written = 0;234 self.columns_written = 0;
235 }235 }
236236
237 fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: var) void {237 fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
238 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {238 if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
239 const amt = written.len;239 const amt = written.len;
240 end.* += amt;240 end.* += amt;
lib/std/segmented_list.zig+2-2
...@@ -122,7 +122,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -122,7 +122,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
122 self.* = undefined;122 self.* = undefined;
123 }123 }
124124
125 pub fn at(self: var, i: usize) AtType(@TypeOf(self)) {125 pub fn at(self: anytype, i: usize) AtType(@TypeOf(self)) {
126 assert(i < self.len);126 assert(i < self.len);
127 return self.uncheckedAt(i);127 return self.uncheckedAt(i);
128 }128 }
...@@ -241,7 +241,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -241,7 +241,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
241 }241 }
242 }242 }
243243
244 pub fn uncheckedAt(self: var, index: usize) AtType(@TypeOf(self)) {244 pub fn uncheckedAt(self: anytype, index: usize) AtType(@TypeOf(self)) {
245 if (index < prealloc_item_count) {245 if (index < prealloc_item_count) {
246 return &self.prealloc_segment[index];246 return &self.prealloc_segment[index];
247 }247 }
lib/std/sort.zig+19-19
...@@ -9,7 +9,7 @@ pub fn binarySearch(...@@ -9,7 +9,7 @@ pub fn binarySearch(
9 comptime T: type,9 comptime T: type,
10 key: T,10 key: T,
11 items: []const T,11 items: []const T,
12 context: var,12 context: anytype,
13 comptime compareFn: fn (context: @TypeOf(context), lhs: T, rhs: T) math.Order,13 comptime compareFn: fn (context: @TypeOf(context), lhs: T, rhs: T) math.Order,
14) ?usize {14) ?usize {
15 var left: usize = 0;15 var left: usize = 0;
...@@ -76,7 +76,7 @@ test "binarySearch" {...@@ -76,7 +76,7 @@ test "binarySearch" {
76pub fn insertionSort(76pub fn insertionSort(
77 comptime T: type,77 comptime T: type,
78 items: []T,78 items: []T,
79 context: var,79 context: anytype,
80 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,80 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
81) void {81) void {
82 var i: usize = 1;82 var i: usize = 1;
...@@ -182,7 +182,7 @@ const Pull = struct {...@@ -182,7 +182,7 @@ const Pull = struct {
182pub fn sort(182pub fn sort(
183 comptime T: type,183 comptime T: type,
184 items: []T,184 items: []T,
185 context: var,185 context: anytype,
186 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,186 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
187) void {187) void {
188 // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c188 // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c
...@@ -813,7 +813,7 @@ fn mergeInPlace(...@@ -813,7 +813,7 @@ fn mergeInPlace(
813 items: []T,813 items: []T,
814 A_arg: Range,814 A_arg: Range,
815 B_arg: Range,815 B_arg: Range,
816 context: var,816 context: anytype,
817 comptime lessThan: fn (@TypeOf(context), T, T) bool,817 comptime lessThan: fn (@TypeOf(context), T, T) bool,
818) void {818) void {
819 if (A_arg.length() == 0 or B_arg.length() == 0) return;819 if (A_arg.length() == 0 or B_arg.length() == 0) return;
...@@ -862,7 +862,7 @@ fn mergeInternal(...@@ -862,7 +862,7 @@ fn mergeInternal(
862 items: []T,862 items: []T,
863 A: Range,863 A: Range,
864 B: Range,864 B: Range,
865 context: var,865 context: anytype,
866 comptime lessThan: fn (@TypeOf(context), T, T) bool,866 comptime lessThan: fn (@TypeOf(context), T, T) bool,
867 buffer: Range,867 buffer: Range,
868) void {868) void {
...@@ -906,7 +906,7 @@ fn findFirstForward(...@@ -906,7 +906,7 @@ fn findFirstForward(
906 items: []T,906 items: []T,
907 value: T,907 value: T,
908 range: Range,908 range: Range,
909 context: var,909 context: anytype,
910 comptime lessThan: fn (@TypeOf(context), T, T) bool,910 comptime lessThan: fn (@TypeOf(context), T, T) bool,
911 unique: usize,911 unique: usize,
912) usize {912) usize {
...@@ -928,7 +928,7 @@ fn findFirstBackward(...@@ -928,7 +928,7 @@ fn findFirstBackward(
928 items: []T,928 items: []T,
929 value: T,929 value: T,
930 range: Range,930 range: Range,
931 context: var,931 context: anytype,
932 comptime lessThan: fn (@TypeOf(context), T, T) bool,932 comptime lessThan: fn (@TypeOf(context), T, T) bool,
933 unique: usize,933 unique: usize,
934) usize {934) usize {
...@@ -950,7 +950,7 @@ fn findLastForward(...@@ -950,7 +950,7 @@ fn findLastForward(
950 items: []T,950 items: []T,
951 value: T,951 value: T,
952 range: Range,952 range: Range,
953 context: var,953 context: anytype,
954 comptime lessThan: fn (@TypeOf(context), T, T) bool,954 comptime lessThan: fn (@TypeOf(context), T, T) bool,
955 unique: usize,955 unique: usize,
956) usize {956) usize {
...@@ -972,7 +972,7 @@ fn findLastBackward(...@@ -972,7 +972,7 @@ fn findLastBackward(
972 items: []T,972 items: []T,
973 value: T,973 value: T,
974 range: Range,974 range: Range,
975 context: var,975 context: anytype,
976 comptime lessThan: fn (@TypeOf(context), T, T) bool,976 comptime lessThan: fn (@TypeOf(context), T, T) bool,
977 unique: usize,977 unique: usize,
978) usize {978) usize {
...@@ -994,7 +994,7 @@ fn binaryFirst(...@@ -994,7 +994,7 @@ fn binaryFirst(
994 items: []T,994 items: []T,
995 value: T,995 value: T,
996 range: Range,996 range: Range,
997 context: var,997 context: anytype,
998 comptime lessThan: fn (@TypeOf(context), T, T) bool,998 comptime lessThan: fn (@TypeOf(context), T, T) bool,
999) usize {999) usize {
1000 var curr = range.start;1000 var curr = range.start;
...@@ -1017,7 +1017,7 @@ fn binaryLast(...@@ -1017,7 +1017,7 @@ fn binaryLast(
1017 items: []T,1017 items: []T,
1018 value: T,1018 value: T,
1019 range: Range,1019 range: Range,
1020 context: var,1020 context: anytype,
1021 comptime lessThan: fn (@TypeOf(context), T, T) bool,1021 comptime lessThan: fn (@TypeOf(context), T, T) bool,
1022) usize {1022) usize {
1023 var curr = range.start;1023 var curr = range.start;
...@@ -1040,7 +1040,7 @@ fn mergeInto(...@@ -1040,7 +1040,7 @@ fn mergeInto(
1040 from: []T,1040 from: []T,
1041 A: Range,1041 A: Range,
1042 B: Range,1042 B: Range,
1043 context: var,1043 context: anytype,
1044 comptime lessThan: fn (@TypeOf(context), T, T) bool,1044 comptime lessThan: fn (@TypeOf(context), T, T) bool,
1045 into: []T,1045 into: []T,
1046) void {1046) void {
...@@ -1078,7 +1078,7 @@ fn mergeExternal(...@@ -1078,7 +1078,7 @@ fn mergeExternal(
1078 items: []T,1078 items: []T,
1079 A: Range,1079 A: Range,
1080 B: Range,1080 B: Range,
1081 context: var,1081 context: anytype,
1082 comptime lessThan: fn (@TypeOf(context), T, T) bool,1082 comptime lessThan: fn (@TypeOf(context), T, T) bool,
1083 cache: []T,1083 cache: []T,
1084) void {1084) void {
...@@ -1112,7 +1112,7 @@ fn mergeExternal(...@@ -1112,7 +1112,7 @@ fn mergeExternal(
1112fn swap(1112fn swap(
1113 comptime T: type,1113 comptime T: type,
1114 items: []T,1114 items: []T,
1115 context: var,1115 context: anytype,
1116 comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool,1116 comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool,
1117 order: *[8]u8,1117 order: *[8]u8,
1118 x: usize,1118 x: usize,
...@@ -1358,7 +1358,7 @@ fn fuzzTest(rng: *std.rand.Random) !void {...@@ -1358,7 +1358,7 @@ fn fuzzTest(rng: *std.rand.Random) !void {
1358pub fn argMin(1358pub fn argMin(
1359 comptime T: type,1359 comptime T: type,
1360 items: []const T,1360 items: []const T,
1361 context: var,1361 context: anytype,
1362 comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool,1362 comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool,
1363) ?usize {1363) ?usize {
1364 if (items.len == 0) {1364 if (items.len == 0) {
...@@ -1390,7 +1390,7 @@ test "argMin" {...@@ -1390,7 +1390,7 @@ test "argMin" {
1390pub fn min(1390pub fn min(
1391 comptime T: type,1391 comptime T: type,
1392 items: []const T,1392 items: []const T,
1393 context: var,1393 context: anytype,
1394 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,1394 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1395) ?T {1395) ?T {
1396 const i = argMin(T, items, context, lessThan) orelse return null;1396 const i = argMin(T, items, context, lessThan) orelse return null;
...@@ -1410,7 +1410,7 @@ test "min" {...@@ -1410,7 +1410,7 @@ test "min" {
1410pub fn argMax(1410pub fn argMax(
1411 comptime T: type,1411 comptime T: type,
1412 items: []const T,1412 items: []const T,
1413 context: var,1413 context: anytype,
1414 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,1414 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1415) ?usize {1415) ?usize {
1416 if (items.len == 0) {1416 if (items.len == 0) {
...@@ -1442,7 +1442,7 @@ test "argMax" {...@@ -1442,7 +1442,7 @@ test "argMax" {
1442pub fn max(1442pub fn max(
1443 comptime T: type,1443 comptime T: type,
1444 items: []const T,1444 items: []const T,
1445 context: var,1445 context: anytype,
1446 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,1446 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1447) ?T {1447) ?T {
1448 const i = argMax(T, items, context, lessThan) orelse return null;1448 const i = argMax(T, items, context, lessThan) orelse return null;
...@@ -1462,7 +1462,7 @@ test "max" {...@@ -1462,7 +1462,7 @@ test "max" {
1462pub fn isSorted(1462pub fn isSorted(
1463 comptime T: type,1463 comptime T: type,
1464 items: []const T,1464 items: []const T,
1465 context: var,1465 context: anytype,
1466 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,1466 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1467) bool {1467) bool {
1468 var i: usize = 1;1468 var i: usize = 1;
lib/std/special/build_runner.zig+2-2
...@@ -135,7 +135,7 @@ fn runBuild(builder: *Builder) anyerror!void {...@@ -135,7 +135,7 @@ fn runBuild(builder: *Builder) anyerror!void {
135 }135 }
136}136}
137137
138fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {138fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
139 // run the build script to collect the options139 // run the build script to collect the options
140 if (!already_ran_build) {140 if (!already_ran_build) {
141 builder.setInstallPrefix(null);141 builder.setInstallPrefix(null);
...@@ -202,7 +202,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {...@@ -202,7 +202,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
202 );202 );
203}203}
204204
205fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: var) void {205fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: anytype) void {
206 usage(builder, already_ran_build, out_stream) catch {};206 usage(builder, already_ran_build, out_stream) catch {};
207 process.exit(1);207 process.exit(1);
208}208}
lib/std/special/compiler_rt/clzsi2_test.zig+1-1
...@@ -4,7 +4,7 @@ const testing = @import("std").testing;...@@ -4,7 +4,7 @@ const testing = @import("std").testing;
4fn test__clzsi2(a: u32, expected: i32) void {4fn test__clzsi2(a: u32, expected: i32) void {
5 var nakedClzsi2 = clzsi2.__clzsi2;5 var nakedClzsi2 = clzsi2.__clzsi2;
6 var actualClzsi2 = @ptrCast(fn (a: i32) callconv(.C) i32, nakedClzsi2);6 var actualClzsi2 = @ptrCast(fn (a: i32) callconv(.C) i32, nakedClzsi2);
7 var x = @intCast(i32, a);7 var x = @bitCast(i32, a);
8 var result = actualClzsi2(x);8 var result = actualClzsi2(x);
9 testing.expectEqual(expected, result);9 testing.expectEqual(expected, result);
10}10}
lib/std/special/compiler_rt/int.zig+1-1
...@@ -244,7 +244,7 @@ pub fn __udivsi3(n: u32, d: u32) callconv(.C) u32 {...@@ -244,7 +244,7 @@ pub fn __udivsi3(n: u32, d: u32) callconv(.C) u32 {
244 // r.all -= d.all;244 // r.all -= d.all;
245 // carry = 1;245 // carry = 1;
246 // }246 // }
247 const s = @intCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1);247 const s = @bitCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1);
248 carry = @intCast(u32, s & 1);248 carry = @intCast(u32, s & 1);
249 r -= d & @bitCast(u32, s);249 r -= d & @bitCast(u32, s);
250 }250 }
lib/std/special/compiler_rt/udivmod.zig+1-1
...@@ -184,7 +184,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:...@@ -184,7 +184,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:
184 // carry = 1;184 // carry = 1;
185 // }185 // }
186 r_all = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421186 r_all = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421
187 const s: SignedDoubleInt = @intCast(SignedDoubleInt, b -% r_all -% 1) >> (DoubleInt.bit_count - 1);187 const s: SignedDoubleInt = @bitCast(SignedDoubleInt, b -% r_all -% 1) >> (DoubleInt.bit_count - 1);
188 carry = @intCast(u32, s & 1);188 carry = @intCast(u32, s & 1);
189 r_all -= b & @bitCast(DoubleInt, s);189 r_all -= b & @bitCast(DoubleInt, s);
190 r = @ptrCast(*[2]SingleInt, &r_all).*; // TODO issue #421190 r = @ptrCast(*[2]SingleInt, &r_all).*; // TODO issue #421
lib/std/special/test_runner.zig+13-1
...@@ -21,6 +21,7 @@ pub fn main() anyerror!void {...@@ -21,6 +21,7 @@ pub fn main() anyerror!void {
2121
22 for (test_fn_list) |test_fn, i| {22 for (test_fn_list) |test_fn, i| {
23 std.testing.base_allocator_instance.reset();23 std.testing.base_allocator_instance.reset();
24 std.testing.log_level = .warn;
2425
25 var test_node = root_node.start(test_fn.name, null);26 var test_node = root_node.start(test_fn.name, null);
26 test_node.activate();27 test_node.activate();
...@@ -35,7 +36,7 @@ pub fn main() anyerror!void {...@@ -35,7 +36,7 @@ pub fn main() anyerror!void {
35 async_frame_buffer = try std.heap.page_allocator.alignedAlloc(u8, std.Target.stack_align, size);36 async_frame_buffer = try std.heap.page_allocator.alignedAlloc(u8, std.Target.stack_align, size);
36 }37 }
37 const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func);38 const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func);
38 break :blk await @asyncCall(async_frame_buffer, {}, casted_fn);39 break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{});
39 },40 },
40 .blocking => {41 .blocking => {
41 skip_count += 1;42 skip_count += 1;
...@@ -73,3 +74,14 @@ pub fn main() anyerror!void {...@@ -73,3 +74,14 @@ pub fn main() anyerror!void {
73 std.debug.warn("{} passed; {} skipped.\n", .{ ok_count, skip_count });74 std.debug.warn("{} passed; {} skipped.\n", .{ ok_count, skip_count });
74 }75 }
75}76}
77
78pub fn log(
79 comptime message_level: std.log.Level,
80 comptime scope: @Type(.EnumLiteral),
81 comptime format: []const u8,
82 args: anytype,
83) void {
84 if (@enumToInt(message_level) <= @enumToInt(std.testing.log_level)) {
85 std.debug.print("[{}] ({}): " ++ format, .{ @tagName(scope), @tagName(message_level) } ++ args);
86 }
87}
lib/std/start.zig+1-1
...@@ -246,7 +246,7 @@ inline fn initEventLoopAndCallMain(comptime Out: type, comptime mainFunc: fn ()...@@ -246,7 +246,7 @@ inline fn initEventLoopAndCallMain(comptime Out: type, comptime mainFunc: fn ()
246246
247 var result: u8 = undefined;247 var result: u8 = undefined;
248 var frame: @Frame(callMainAsync) = undefined;248 var frame: @Frame(callMainAsync) = undefined;
249 _ = @asyncCall(&frame, &result, callMainAsync, u8, mainFunc, loop);249 _ = @asyncCall(&frame, &result, callMainAsync, .{u8, mainFunc, loop});
250 loop.run();250 loop.run();
251 return result;251 return result;
252 }252 }
lib/std/std.zig+7-3
...@@ -3,14 +3,16 @@ pub const ArrayListAligned = @import("array_list.zig").ArrayListAligned;...@@ -3,14 +3,16 @@ pub const ArrayListAligned = @import("array_list.zig").ArrayListAligned;
3pub const ArrayListAlignedUnmanaged = @import("array_list.zig").ArrayListAlignedUnmanaged;3pub const ArrayListAlignedUnmanaged = @import("array_list.zig").ArrayListAlignedUnmanaged;
4pub const ArrayListSentineled = @import("array_list_sentineled.zig").ArrayListSentineled;4pub const ArrayListSentineled = @import("array_list_sentineled.zig").ArrayListSentineled;
5pub const ArrayListUnmanaged = @import("array_list.zig").ArrayListUnmanaged;5pub const ArrayListUnmanaged = @import("array_list.zig").ArrayListUnmanaged;
6pub const AutoHashMap = @import("hash_map.zig").AutoHashMap;6pub const AutoHashMap = hash_map.AutoHashMap;
7pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;
7pub const BloomFilter = @import("bloom_filter.zig").BloomFilter;8pub const BloomFilter = @import("bloom_filter.zig").BloomFilter;
8pub const BufMap = @import("buf_map.zig").BufMap;9pub const BufMap = @import("buf_map.zig").BufMap;
9pub const BufSet = @import("buf_set.zig").BufSet;10pub const BufSet = @import("buf_set.zig").BufSet;
10pub const ChildProcess = @import("child_process.zig").ChildProcess;11pub const ChildProcess = @import("child_process.zig").ChildProcess;
11pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap;12pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap;
12pub const DynLib = @import("dynamic_library.zig").DynLib;13pub const DynLib = @import("dynamic_library.zig").DynLib;
13pub const HashMap = @import("hash_map.zig").HashMap;14pub const HashMap = hash_map.HashMap;
15pub const HashMapUnmanaged = hash_map.HashMapUnmanaged;
14pub const Mutex = @import("mutex.zig").Mutex;16pub const Mutex = @import("mutex.zig").Mutex;
15pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;17pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
16pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;18pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
...@@ -22,7 +24,8 @@ pub const ResetEvent = @import("reset_event.zig").ResetEvent;...@@ -22,7 +24,8 @@ pub const ResetEvent = @import("reset_event.zig").ResetEvent;
22pub const SegmentedList = @import("segmented_list.zig").SegmentedList;24pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
23pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;25pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
24pub const SpinLock = @import("spinlock.zig").SpinLock;26pub const SpinLock = @import("spinlock.zig").SpinLock;
25pub const StringHashMap = @import("hash_map.zig").StringHashMap;27pub const StringHashMap = hash_map.StringHashMap;
28pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged;
26pub const TailQueue = @import("linked_list.zig").TailQueue;29pub const TailQueue = @import("linked_list.zig").TailQueue;
27pub const Target = @import("target.zig").Target;30pub const Target = @import("target.zig").Target;
28pub const Thread = @import("thread.zig").Thread;31pub const Thread = @import("thread.zig").Thread;
...@@ -49,6 +52,7 @@ pub const heap = @import("heap.zig");...@@ -49,6 +52,7 @@ pub const heap = @import("heap.zig");
49pub const http = @import("http.zig");52pub const http = @import("http.zig");
50pub const io = @import("io.zig");53pub const io = @import("io.zig");
51pub const json = @import("json.zig");54pub const json = @import("json.zig");
55pub const log = @import("log.zig");
52pub const macho = @import("macho.zig");56pub const macho = @import("macho.zig");
53pub const math = @import("math.zig");57pub const math = @import("math.zig");
54pub const mem = @import("mem.zig");58pub const mem = @import("mem.zig");
lib/std/target.zig+51-16
...@@ -101,6 +101,31 @@ pub const Target = struct {...@@ -101,6 +101,31 @@ pub const Target = struct {
101 return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max);101 return @enumToInt(ver) >= @enumToInt(self.min) and @enumToInt(ver) <= @enumToInt(self.max);
102 }102 }
103 };103 };
104
105 /// This function is defined to serialize a Zig source code representation of this
106 /// type, that, when parsed, will deserialize into the same data.
107 pub fn format(
108 self: WindowsVersion,
109 comptime fmt: []const u8,
110 options: std.fmt.FormatOptions,
111 out_stream: anytype,
112 ) !void {
113 if (fmt.len > 0 and fmt[0] == 's') {
114 if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.win10_19h1)) {
115 try std.fmt.format(out_stream, ".{}", .{@tagName(self)});
116 } else {
117 try std.fmt.format(out_stream, "@intToEnum(Target.Os.WindowsVersion, {})", .{@enumToInt(self)});
118 }
119 } else {
120 if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.win10_19h1)) {
121 try std.fmt.format(out_stream, "WindowsVersion.{}", .{@tagName(self)});
122 } else {
123 try std.fmt.format(out_stream, "WindowsVersion(", .{@typeName(@This())});
124 try std.fmt.format(out_stream, "{}", .{@enumToInt(self)});
125 try out_stream.writeAll(")");
126 }
127 }
128 }
104 };129 };
105130
106 pub const LinuxVersionRange = struct {131 pub const LinuxVersionRange = struct {
...@@ -410,6 +435,7 @@ pub const Target = struct {...@@ -410,6 +435,7 @@ pub const Target = struct {
410 elf,435 elf,
411 macho,436 macho,
412 wasm,437 wasm,
438 c,
413 };439 };
414440
415 pub const SubSystem = enum {441 pub const SubSystem = enum {
...@@ -871,25 +897,34 @@ pub const Target = struct {...@@ -871,25 +897,34 @@ pub const Target = struct {
871 /// All processors Zig is aware of, sorted lexicographically by name.897 /// All processors Zig is aware of, sorted lexicographically by name.
872 pub fn allCpuModels(arch: Arch) []const *const Cpu.Model {898 pub fn allCpuModels(arch: Arch) []const *const Cpu.Model {
873 return switch (arch) {899 return switch (arch) {
874 .arm, .armeb, .thumb, .thumbeb => arm.all_cpus,900 .arm, .armeb, .thumb, .thumbeb => comptime allCpusFromDecls(arm.cpu),
875 .aarch64, .aarch64_be, .aarch64_32 => aarch64.all_cpus,901 .aarch64, .aarch64_be, .aarch64_32 => comptime allCpusFromDecls(aarch64.cpu),
876 .avr => avr.all_cpus,902 .avr => comptime allCpusFromDecls(avr.cpu),
877 .bpfel, .bpfeb => bpf.all_cpus,903 .bpfel, .bpfeb => comptime allCpusFromDecls(bpf.cpu),
878 .hexagon => hexagon.all_cpus,904 .hexagon => comptime allCpusFromDecls(hexagon.cpu),
879 .mips, .mipsel, .mips64, .mips64el => mips.all_cpus,905 .mips, .mipsel, .mips64, .mips64el => comptime allCpusFromDecls(mips.cpu),
880 .msp430 => msp430.all_cpus,906 .msp430 => comptime allCpusFromDecls(msp430.cpu),
881 .powerpc, .powerpc64, .powerpc64le => powerpc.all_cpus,907 .powerpc, .powerpc64, .powerpc64le => comptime allCpusFromDecls(powerpc.cpu),
882 .amdgcn => amdgpu.all_cpus,908 .amdgcn => comptime allCpusFromDecls(amdgpu.cpu),
883 .riscv32, .riscv64 => riscv.all_cpus,909 .riscv32, .riscv64 => comptime allCpusFromDecls(riscv.cpu),
884 .sparc, .sparcv9, .sparcel => sparc.all_cpus,910 .sparc, .sparcv9, .sparcel => comptime allCpusFromDecls(sparc.cpu),
885 .s390x => systemz.all_cpus,911 .s390x => comptime allCpusFromDecls(systemz.cpu),
886 .i386, .x86_64 => x86.all_cpus,912 .i386, .x86_64 => comptime allCpusFromDecls(x86.cpu),
887 .nvptx, .nvptx64 => nvptx.all_cpus,913 .nvptx, .nvptx64 => comptime allCpusFromDecls(nvptx.cpu),
888 .wasm32, .wasm64 => wasm.all_cpus,914 .wasm32, .wasm64 => comptime allCpusFromDecls(wasm.cpu),
889915
890 else => &[0]*const Model{},916 else => &[0]*const Model{},
891 };917 };
892 }918 }
919
920 fn allCpusFromDecls(comptime cpus: type) []const *const Cpu.Model {
921 const decls = std.meta.declarations(cpus);
922 var array: [decls.len]*const Cpu.Model = undefined;
923 for (decls) |decl, i| {
924 array[i] = &@field(cpus, decl.name);
925 }
926 return &array;
927 }
893 };928 };
894929
895 pub const Model = struct {930 pub const Model = struct {
...@@ -1157,7 +1192,7 @@ pub const Target = struct {...@@ -1157,7 +1192,7 @@ pub const Target = struct {
1157 pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {1192 pub fn standardDynamicLinkerPath(self: Target) DynamicLinker {
1158 var result: DynamicLinker = .{};1193 var result: DynamicLinker = .{};
1159 const S = struct {1194 const S = struct {
1160 fn print(r: *DynamicLinker, comptime fmt: []const u8, args: var) DynamicLinker {1195 fn print(r: *DynamicLinker, comptime fmt: []const u8, args: anytype) DynamicLinker {
1161 r.max_byte = @intCast(u8, (std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1);1196 r.max_byte = @intCast(u8, (std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1);
1162 return r.*;1197 return r.*;
1163 }1198 }
lib/std/target/aarch64.zig-45
...@@ -1505,48 +1505,3 @@ pub const cpu = struct {...@@ -1505,48 +1505,3 @@ pub const cpu = struct {
1505 }),1505 }),
1506 };1506 };
1507};1507};
1508
1509/// All aarch64 CPUs, sorted alphabetically by name.
1510/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
1511/// compiler has inefficient memory and CPU usage, affecting build times.
1512pub const all_cpus = &[_]*const CpuModel{
1513 &cpu.apple_a10,
1514 &cpu.apple_a11,
1515 &cpu.apple_a12,
1516 &cpu.apple_a13,
1517 &cpu.apple_a7,
1518 &cpu.apple_a8,
1519 &cpu.apple_a9,
1520 &cpu.apple_latest,
1521 &cpu.apple_s4,
1522 &cpu.apple_s5,
1523 &cpu.cortex_a35,
1524 &cpu.cortex_a53,
1525 &cpu.cortex_a55,
1526 &cpu.cortex_a57,
1527 &cpu.cortex_a65,
1528 &cpu.cortex_a65ae,
1529 &cpu.cortex_a72,
1530 &cpu.cortex_a73,
1531 &cpu.cortex_a75,
1532 &cpu.cortex_a76,
1533 &cpu.cortex_a76ae,
1534 &cpu.cyclone,
1535 &cpu.exynos_m1,
1536 &cpu.exynos_m2,
1537 &cpu.exynos_m3,
1538 &cpu.exynos_m4,
1539 &cpu.exynos_m5,
1540 &cpu.falkor,
1541 &cpu.generic,
1542 &cpu.kryo,
1543 &cpu.neoverse_e1,
1544 &cpu.neoverse_n1,
1545 &cpu.saphira,
1546 &cpu.thunderx,
1547 &cpu.thunderx2t99,
1548 &cpu.thunderxt81,
1549 &cpu.thunderxt83,
1550 &cpu.thunderxt88,
1551 &cpu.tsv110,
1552};
lib/std/target/amdgpu.zig-45
...@@ -1276,48 +1276,3 @@ pub const cpu = struct {...@@ -1276,48 +1276,3 @@ pub const cpu = struct {
1276 }),1276 }),
1277 };1277 };
1278};1278};
1279
1280/// All amdgpu CPUs, sorted alphabetically by name.
1281/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
1282/// compiler has inefficient memory and CPU usage, affecting build times.
1283pub const all_cpus = &[_]*const CpuModel{
1284 &cpu.bonaire,
1285 &cpu.carrizo,
1286 &cpu.fiji,
1287 &cpu.generic,
1288 &cpu.generic_hsa,
1289 &cpu.gfx1010,
1290 &cpu.gfx1011,
1291 &cpu.gfx1012,
1292 &cpu.gfx600,
1293 &cpu.gfx601,
1294 &cpu.gfx700,
1295 &cpu.gfx701,
1296 &cpu.gfx702,
1297 &cpu.gfx703,
1298 &cpu.gfx704,
1299 &cpu.gfx801,
1300 &cpu.gfx802,
1301 &cpu.gfx803,
1302 &cpu.gfx810,
1303 &cpu.gfx900,
1304 &cpu.gfx902,
1305 &cpu.gfx904,
1306 &cpu.gfx906,
1307 &cpu.gfx908,
1308 &cpu.gfx909,
1309 &cpu.hainan,
1310 &cpu.hawaii,
1311 &cpu.iceland,
1312 &cpu.kabini,
1313 &cpu.kaveri,
1314 &cpu.mullins,
1315 &cpu.oland,
1316 &cpu.pitcairn,
1317 &cpu.polaris10,
1318 &cpu.polaris11,
1319 &cpu.stoney,
1320 &cpu.tahiti,
1321 &cpu.tonga,
1322 &cpu.verde,
1323};
lib/std/target/arm.zig-89
...@@ -2145,92 +2145,3 @@ pub const cpu = struct {...@@ -2145,92 +2145,3 @@ pub const cpu = struct {
2145 }),2145 }),
2146 };2146 };
2147};2147};
2148
2149/// All arm CPUs, sorted alphabetically by name.
2150/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
2151/// compiler has inefficient memory and CPU usage, affecting build times.
2152pub const all_cpus = &[_]*const CpuModel{
2153 &cpu.arm1020e,
2154 &cpu.arm1020t,
2155 &cpu.arm1022e,
2156 &cpu.arm10e,
2157 &cpu.arm10tdmi,
2158 &cpu.arm1136j_s,
2159 &cpu.arm1136jf_s,
2160 &cpu.arm1156t2_s,
2161 &cpu.arm1156t2f_s,
2162 &cpu.arm1176j_s,
2163 &cpu.arm1176jz_s,
2164 &cpu.arm1176jzf_s,
2165 &cpu.arm710t,
2166 &cpu.arm720t,
2167 &cpu.arm7tdmi,
2168 &cpu.arm7tdmi_s,
2169 &cpu.arm8,
2170 &cpu.arm810,
2171 &cpu.arm9,
2172 &cpu.arm920,
2173 &cpu.arm920t,
2174 &cpu.arm922t,
2175 &cpu.arm926ej_s,
2176 &cpu.arm940t,
2177 &cpu.arm946e_s,
2178 &cpu.arm966e_s,
2179 &cpu.arm968e_s,
2180 &cpu.arm9e,
2181 &cpu.arm9tdmi,
2182 &cpu.cortex_a12,
2183 &cpu.cortex_a15,
2184 &cpu.cortex_a17,
2185 &cpu.cortex_a32,
2186 &cpu.cortex_a35,
2187 &cpu.cortex_a5,
2188 &cpu.cortex_a53,
2189 &cpu.cortex_a55,
2190 &cpu.cortex_a57,
2191 &cpu.cortex_a7,
2192 &cpu.cortex_a72,
2193 &cpu.cortex_a73,
2194 &cpu.cortex_a75,
2195 &cpu.cortex_a76,
2196 &cpu.cortex_a76ae,
2197 &cpu.cortex_a8,
2198 &cpu.cortex_a9,
2199 &cpu.cortex_m0,
2200 &cpu.cortex_m0plus,
2201 &cpu.cortex_m1,
2202 &cpu.cortex_m23,
2203 &cpu.cortex_m3,
2204 &cpu.cortex_m33,
2205 &cpu.cortex_m35p,
2206 &cpu.cortex_m4,
2207 &cpu.cortex_m7,
2208 &cpu.cortex_r4,
2209 &cpu.cortex_r4f,
2210 &cpu.cortex_r5,
2211 &cpu.cortex_r52,
2212 &cpu.cortex_r7,
2213 &cpu.cortex_r8,
2214 &cpu.cyclone,
2215 &cpu.ep9312,
2216 &cpu.exynos_m1,
2217 &cpu.exynos_m2,
2218 &cpu.exynos_m3,
2219 &cpu.exynos_m4,
2220 &cpu.exynos_m5,
2221 &cpu.generic,
2222 &cpu.iwmmxt,
2223 &cpu.krait,
2224 &cpu.kryo,
2225 &cpu.mpcore,
2226 &cpu.mpcorenovfp,
2227 &cpu.neoverse_n1,
2228 &cpu.sc000,
2229 &cpu.sc300,
2230 &cpu.strongarm,
2231 &cpu.strongarm110,
2232 &cpu.strongarm1100,
2233 &cpu.strongarm1110,
2234 &cpu.swift,
2235 &cpu.xscale,
2236};
lib/std/target/avr.zig-263
...@@ -2116,266 +2116,3 @@ pub const cpu = struct {...@@ -2116,266 +2116,3 @@ pub const cpu = struct {
2116 }),2116 }),
2117 };2117 };
2118};2118};
2119
2120/// All avr CPUs, sorted alphabetically by name.
2121/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
2122/// compiler has inefficient memory and CPU usage, affecting build times.
2123pub const all_cpus = &[_]*const CpuModel{
2124 &cpu.at43usb320,
2125 &cpu.at43usb355,
2126 &cpu.at76c711,
2127 &cpu.at86rf401,
2128 &cpu.at90c8534,
2129 &cpu.at90can128,
2130 &cpu.at90can32,
2131 &cpu.at90can64,
2132 &cpu.at90pwm1,
2133 &cpu.at90pwm161,
2134 &cpu.at90pwm2,
2135 &cpu.at90pwm216,
2136 &cpu.at90pwm2b,
2137 &cpu.at90pwm3,
2138 &cpu.at90pwm316,
2139 &cpu.at90pwm3b,
2140 &cpu.at90pwm81,
2141 &cpu.at90s1200,
2142 &cpu.at90s2313,
2143 &cpu.at90s2323,
2144 &cpu.at90s2333,
2145 &cpu.at90s2343,
2146 &cpu.at90s4414,
2147 &cpu.at90s4433,
2148 &cpu.at90s4434,
2149 &cpu.at90s8515,
2150 &cpu.at90s8535,
2151 &cpu.at90scr100,
2152 &cpu.at90usb1286,
2153 &cpu.at90usb1287,
2154 &cpu.at90usb162,
2155 &cpu.at90usb646,
2156 &cpu.at90usb647,
2157 &cpu.at90usb82,
2158 &cpu.at94k,
2159 &cpu.ata5272,
2160 &cpu.ata5505,
2161 &cpu.ata5790,
2162 &cpu.ata5795,
2163 &cpu.ata6285,
2164 &cpu.ata6286,
2165 &cpu.ata6289,
2166 &cpu.atmega103,
2167 &cpu.atmega128,
2168 &cpu.atmega1280,
2169 &cpu.atmega1281,
2170 &cpu.atmega1284,
2171 &cpu.atmega1284p,
2172 &cpu.atmega1284rfr2,
2173 &cpu.atmega128a,
2174 &cpu.atmega128rfa1,
2175 &cpu.atmega128rfr2,
2176 &cpu.atmega16,
2177 &cpu.atmega161,
2178 &cpu.atmega162,
2179 &cpu.atmega163,
2180 &cpu.atmega164a,
2181 &cpu.atmega164p,
2182 &cpu.atmega164pa,
2183 &cpu.atmega165,
2184 &cpu.atmega165a,
2185 &cpu.atmega165p,
2186 &cpu.atmega165pa,
2187 &cpu.atmega168,
2188 &cpu.atmega168a,
2189 &cpu.atmega168p,
2190 &cpu.atmega168pa,
2191 &cpu.atmega169,
2192 &cpu.atmega169a,
2193 &cpu.atmega169p,
2194 &cpu.atmega169pa,
2195 &cpu.atmega16a,
2196 &cpu.atmega16hva,
2197 &cpu.atmega16hva2,
2198 &cpu.atmega16hvb,
2199 &cpu.atmega16hvbrevb,
2200 &cpu.atmega16m1,
2201 &cpu.atmega16u2,
2202 &cpu.atmega16u4,
2203 &cpu.atmega2560,
2204 &cpu.atmega2561,
2205 &cpu.atmega2564rfr2,
2206 &cpu.atmega256rfr2,
2207 &cpu.atmega32,
2208 &cpu.atmega323,
2209 &cpu.atmega324a,
2210 &cpu.atmega324p,
2211 &cpu.atmega324pa,
2212 &cpu.atmega325,
2213 &cpu.atmega3250,
2214 &cpu.atmega3250a,
2215 &cpu.atmega3250p,
2216 &cpu.atmega3250pa,
2217 &cpu.atmega325a,
2218 &cpu.atmega325p,
2219 &cpu.atmega325pa,
2220 &cpu.atmega328,
2221 &cpu.atmega328p,
2222 &cpu.atmega329,
2223 &cpu.atmega3290,
2224 &cpu.atmega3290a,
2225 &cpu.atmega3290p,
2226 &cpu.atmega3290pa,
2227 &cpu.atmega329a,
2228 &cpu.atmega329p,
2229 &cpu.atmega329pa,
2230 &cpu.atmega32a,
2231 &cpu.atmega32c1,
2232 &cpu.atmega32hvb,
2233 &cpu.atmega32hvbrevb,
2234 &cpu.atmega32m1,
2235 &cpu.atmega32u2,
2236 &cpu.atmega32u4,
2237 &cpu.atmega32u6,
2238 &cpu.atmega406,
2239 &cpu.atmega48,
2240 &cpu.atmega48a,
2241 &cpu.atmega48p,
2242 &cpu.atmega48pa,
2243 &cpu.atmega64,
2244 &cpu.atmega640,
2245 &cpu.atmega644,
2246 &cpu.atmega644a,
2247 &cpu.atmega644p,
2248 &cpu.atmega644pa,
2249 &cpu.atmega644rfr2,
2250 &cpu.atmega645,
2251 &cpu.atmega6450,
2252 &cpu.atmega6450a,
2253 &cpu.atmega6450p,
2254 &cpu.atmega645a,
2255 &cpu.atmega645p,
2256 &cpu.atmega649,
2257 &cpu.atmega6490,
2258 &cpu.atmega6490a,
2259 &cpu.atmega6490p,
2260 &cpu.atmega649a,
2261 &cpu.atmega649p,
2262 &cpu.atmega64a,
2263 &cpu.atmega64c1,
2264 &cpu.atmega64hve,
2265 &cpu.atmega64m1,
2266 &cpu.atmega64rfr2,
2267 &cpu.atmega8,
2268 &cpu.atmega8515,
2269 &cpu.atmega8535,
2270 &cpu.atmega88,
2271 &cpu.atmega88a,
2272 &cpu.atmega88p,
2273 &cpu.atmega88pa,
2274 &cpu.atmega8a,
2275 &cpu.atmega8hva,
2276 &cpu.atmega8u2,
2277 &cpu.attiny10,
2278 &cpu.attiny102,
2279 &cpu.attiny104,
2280 &cpu.attiny11,
2281 &cpu.attiny12,
2282 &cpu.attiny13,
2283 &cpu.attiny13a,
2284 &cpu.attiny15,
2285 &cpu.attiny1634,
2286 &cpu.attiny167,
2287 &cpu.attiny20,
2288 &cpu.attiny22,
2289 &cpu.attiny2313,
2290 &cpu.attiny2313a,
2291 &cpu.attiny24,
2292 &cpu.attiny24a,
2293 &cpu.attiny25,
2294 &cpu.attiny26,
2295 &cpu.attiny261,
2296 &cpu.attiny261a,
2297 &cpu.attiny28,
2298 &cpu.attiny4,
2299 &cpu.attiny40,
2300 &cpu.attiny4313,
2301 &cpu.attiny43u,
2302 &cpu.attiny44,
2303 &cpu.attiny44a,
2304 &cpu.attiny45,
2305 &cpu.attiny461,
2306 &cpu.attiny461a,
2307 &cpu.attiny48,
2308 &cpu.attiny5,
2309 &cpu.attiny828,
2310 &cpu.attiny84,
2311 &cpu.attiny84a,
2312 &cpu.attiny85,
2313 &cpu.attiny861,
2314 &cpu.attiny861a,
2315 &cpu.attiny87,
2316 &cpu.attiny88,
2317 &cpu.attiny9,
2318 &cpu.atxmega128a1,
2319 &cpu.atxmega128a1u,
2320 &cpu.atxmega128a3,
2321 &cpu.atxmega128a3u,
2322 &cpu.atxmega128a4u,
2323 &cpu.atxmega128b1,
2324 &cpu.atxmega128b3,
2325 &cpu.atxmega128c3,
2326 &cpu.atxmega128d3,
2327 &cpu.atxmega128d4,
2328 &cpu.atxmega16a4,
2329 &cpu.atxmega16a4u,
2330 &cpu.atxmega16c4,
2331 &cpu.atxmega16d4,
2332 &cpu.atxmega16e5,
2333 &cpu.atxmega192a3,
2334 &cpu.atxmega192a3u,
2335 &cpu.atxmega192c3,
2336 &cpu.atxmega192d3,
2337 &cpu.atxmega256a3,
2338 &cpu.atxmega256a3b,
2339 &cpu.atxmega256a3bu,
2340 &cpu.atxmega256a3u,
2341 &cpu.atxmega256c3,
2342 &cpu.atxmega256d3,
2343 &cpu.atxmega32a4,
2344 &cpu.atxmega32a4u,
2345 &cpu.atxmega32c4,
2346 &cpu.atxmega32d4,
2347 &cpu.atxmega32e5,
2348 &cpu.atxmega32x1,
2349 &cpu.atxmega384c3,
2350 &cpu.atxmega384d3,
2351 &cpu.atxmega64a1,
2352 &cpu.atxmega64a1u,
2353 &cpu.atxmega64a3,
2354 &cpu.atxmega64a3u,
2355 &cpu.atxmega64a4u,
2356 &cpu.atxmega64b1,
2357 &cpu.atxmega64b3,
2358 &cpu.atxmega64c3,
2359 &cpu.atxmega64d3,
2360 &cpu.atxmega64d4,
2361 &cpu.atxmega8e5,
2362 &cpu.avr1,
2363 &cpu.avr2,
2364 &cpu.avr25,
2365 &cpu.avr3,
2366 &cpu.avr31,
2367 &cpu.avr35,
2368 &cpu.avr4,
2369 &cpu.avr5,
2370 &cpu.avr51,
2371 &cpu.avr6,
2372 &cpu.avrtiny,
2373 &cpu.avrxmega1,
2374 &cpu.avrxmega2,
2375 &cpu.avrxmega3,
2376 &cpu.avrxmega4,
2377 &cpu.avrxmega5,
2378 &cpu.avrxmega6,
2379 &cpu.avrxmega7,
2380 &cpu.m3000,
2381};
lib/std/target/bpf.zig-11
...@@ -64,14 +64,3 @@ pub const cpu = struct {...@@ -64,14 +64,3 @@ pub const cpu = struct {
64 .features = featureSet(&[_]Feature{}),64 .features = featureSet(&[_]Feature{}),
65 };65 };
66};66};
67
68/// All bpf CPUs, sorted alphabetically by name.
69/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
70/// compiler has inefficient memory and CPU usage, affecting build times.
71pub const all_cpus = &[_]*const CpuModel{
72 &cpu.generic,
73 &cpu.probe,
74 &cpu.v1,
75 &cpu.v2,
76 &cpu.v3,
77};
lib/std/target/hexagon.zig-13
...@@ -298,16 +298,3 @@ pub const cpu = struct {...@@ -298,16 +298,3 @@ pub const cpu = struct {
298 }),298 }),
299 };299 };
300};300};
301
302/// All hexagon CPUs, sorted alphabetically by name.
303/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
304/// compiler has inefficient memory and CPU usage, affecting build times.
305pub const all_cpus = &[_]*const CpuModel{
306 &cpu.generic,
307 &cpu.hexagonv5,
308 &cpu.hexagonv55,
309 &cpu.hexagonv60,
310 &cpu.hexagonv62,
311 &cpu.hexagonv65,
312 &cpu.hexagonv66,
313};
lib/std/target/mips.zig-25
...@@ -524,28 +524,3 @@ pub const cpu = struct {...@@ -524,28 +524,3 @@ pub const cpu = struct {
524 }),524 }),
525 };525 };
526};526};
527
528/// All mips CPUs, sorted alphabetically by name.
529/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
530/// compiler has inefficient memory and CPU usage, affecting build times.
531pub const all_cpus = &[_]*const CpuModel{
532 &cpu.generic,
533 &cpu.mips1,
534 &cpu.mips2,
535 &cpu.mips3,
536 &cpu.mips32,
537 &cpu.mips32r2,
538 &cpu.mips32r3,
539 &cpu.mips32r5,
540 &cpu.mips32r6,
541 &cpu.mips4,
542 &cpu.mips5,
543 &cpu.mips64,
544 &cpu.mips64r2,
545 &cpu.mips64r3,
546 &cpu.mips64r5,
547 &cpu.mips64r6,
548 &cpu.octeon,
549 &cpu.@"octeon+",
550 &cpu.p5600,
551};
lib/std/target/msp430.zig-9
...@@ -62,12 +62,3 @@ pub const cpu = struct {...@@ -62,12 +62,3 @@ pub const cpu = struct {
62 }),62 }),
63 };63 };
64};64};
65
66/// All msp430 CPUs, sorted alphabetically by name.
67/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
68/// compiler has inefficient memory and CPU usage, affecting build times.
69pub const all_cpus = &[_]*const CpuModel{
70 &cpu.generic,
71 &cpu.msp430,
72 &cpu.msp430x,
73};
lib/std/target/nvptx.zig-21
...@@ -287,24 +287,3 @@ pub const cpu = struct {...@@ -287,24 +287,3 @@ pub const cpu = struct {
287 }),287 }),
288 };288 };
289};289};
290
291/// All nvptx CPUs, sorted alphabetically by name.
292/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
293/// compiler has inefficient memory and CPU usage, affecting build times.
294pub const all_cpus = &[_]*const CpuModel{
295 &cpu.sm_20,
296 &cpu.sm_21,
297 &cpu.sm_30,
298 &cpu.sm_32,
299 &cpu.sm_35,
300 &cpu.sm_37,
301 &cpu.sm_50,
302 &cpu.sm_52,
303 &cpu.sm_53,
304 &cpu.sm_60,
305 &cpu.sm_61,
306 &cpu.sm_62,
307 &cpu.sm_70,
308 &cpu.sm_72,
309 &cpu.sm_75,
310};
lib/std/target/powerpc.zig-44
...@@ -944,47 +944,3 @@ pub const cpu = struct {...@@ -944,47 +944,3 @@ pub const cpu = struct {
944 }),944 }),
945 };945 };
946};946};
947
948/// All powerpc CPUs, sorted alphabetically by name.
949/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
950/// compiler has inefficient memory and CPU usage, affecting build times.
951pub const all_cpus = &[_]*const CpuModel{
952 &cpu.@"440",
953 &cpu.@"450",
954 &cpu.@"601",
955 &cpu.@"602",
956 &cpu.@"603",
957 &cpu.@"603e",
958 &cpu.@"603ev",
959 &cpu.@"604",
960 &cpu.@"604e",
961 &cpu.@"620",
962 &cpu.@"7400",
963 &cpu.@"7450",
964 &cpu.@"750",
965 &cpu.@"970",
966 &cpu.a2,
967 &cpu.a2q,
968 &cpu.e500,
969 &cpu.e500mc,
970 &cpu.e5500,
971 &cpu.future,
972 &cpu.g3,
973 &cpu.g4,
974 &cpu.@"g4+",
975 &cpu.g5,
976 &cpu.generic,
977 &cpu.ppc,
978 &cpu.ppc32,
979 &cpu.ppc64,
980 &cpu.ppc64le,
981 &cpu.pwr3,
982 &cpu.pwr4,
983 &cpu.pwr5,
984 &cpu.pwr5x,
985 &cpu.pwr6,
986 &cpu.pwr6x,
987 &cpu.pwr7,
988 &cpu.pwr8,
989 &cpu.pwr9,
990};
lib/std/target/riscv.zig-10
...@@ -303,13 +303,3 @@ pub const cpu = struct {...@@ -303,13 +303,3 @@ pub const cpu = struct {
303 }),303 }),
304 };304 };
305};305};
306
307/// All riscv CPUs, sorted alphabetically by name.
308/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
309/// compiler has inefficient memory and CPU usage, affecting build times.
310pub const all_cpus = &[_]*const CpuModel{
311 &cpu.baseline_rv32,
312 &cpu.baseline_rv64,
313 &cpu.generic_rv32,
314 &cpu.generic_rv64,
315};
lib/std/target/sparc.zig-46
...@@ -448,49 +448,3 @@ pub const cpu = struct {...@@ -448,49 +448,3 @@ pub const cpu = struct {
448 }),448 }),
449 };449 };
450};450};
451
452/// All sparc CPUs, sorted alphabetically by name.
453/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
454/// compiler has inefficient memory and CPU usage, affecting build times.
455pub const all_cpus = &[_]*const CpuModel{
456 &cpu.at697e,
457 &cpu.at697f,
458 &cpu.f934,
459 &cpu.generic,
460 &cpu.gr712rc,
461 &cpu.gr740,
462 &cpu.hypersparc,
463 &cpu.leon2,
464 &cpu.leon3,
465 &cpu.leon4,
466 &cpu.ma2080,
467 &cpu.ma2085,
468 &cpu.ma2100,
469 &cpu.ma2150,
470 &cpu.ma2155,
471 &cpu.ma2450,
472 &cpu.ma2455,
473 &cpu.ma2480,
474 &cpu.ma2485,
475 &cpu.ma2x5x,
476 &cpu.ma2x8x,
477 &cpu.myriad2,
478 &cpu.myriad2_1,
479 &cpu.myriad2_2,
480 &cpu.myriad2_3,
481 &cpu.niagara,
482 &cpu.niagara2,
483 &cpu.niagara3,
484 &cpu.niagara4,
485 &cpu.sparclet,
486 &cpu.sparclite,
487 &cpu.sparclite86x,
488 &cpu.supersparc,
489 &cpu.tsc701,
490 &cpu.ultrasparc,
491 &cpu.ultrasparc3,
492 &cpu.ut699,
493 &cpu.v7,
494 &cpu.v8,
495 &cpu.v9,
496};
lib/std/target/systemz.zig-19
...@@ -532,22 +532,3 @@ pub const cpu = struct {...@@ -532,22 +532,3 @@ pub const cpu = struct {
532 }),532 }),
533 };533 };
534};534};
535
536/// All systemz CPUs, sorted alphabetically by name.
537/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
538/// compiler has inefficient memory and CPU usage, affecting build times.
539pub const all_cpus = &[_]*const CpuModel{
540 &cpu.arch10,
541 &cpu.arch11,
542 &cpu.arch12,
543 &cpu.arch13,
544 &cpu.arch8,
545 &cpu.arch9,
546 &cpu.generic,
547 &cpu.z10,
548 &cpu.z13,
549 &cpu.z14,
550 &cpu.z15,
551 &cpu.z196,
552 &cpu.zEC12,
553};
lib/std/target/wasm.zig-9
...@@ -104,12 +104,3 @@ pub const cpu = struct {...@@ -104,12 +104,3 @@ pub const cpu = struct {
104 .features = featureSet(&[_]Feature{}),104 .features = featureSet(&[_]Feature{}),
105 };105 };
106};106};
107
108/// All wasm CPUs, sorted alphabetically by name.
109/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
110/// compiler has inefficient memory and CPU usage, affecting build times.
111pub const all_cpus = &[_]*const CpuModel{
112 &cpu.bleeding_edge,
113 &cpu.generic,
114 &cpu.mvp,
115};
lib/std/target/x86.zig-85
...@@ -2943,88 +2943,3 @@ pub const cpu = struct {...@@ -2943,88 +2943,3 @@ pub const cpu = struct {
2943 }),2943 }),
2944 };2944 };
2945};2945};
2946
2947/// All x86 CPUs, sorted alphabetically by name.
2948/// TODO: Replace this with usage of `std.meta.declList`. It does work, but stage1
2949/// compiler has inefficient memory and CPU usage, affecting build times.
2950pub const all_cpus = &[_]*const CpuModel{
2951 &cpu.amdfam10,
2952 &cpu.athlon,
2953 &cpu.athlon_4,
2954 &cpu.athlon_fx,
2955 &cpu.athlon_mp,
2956 &cpu.athlon_tbird,
2957 &cpu.athlon_xp,
2958 &cpu.athlon64,
2959 &cpu.athlon64_sse3,
2960 &cpu.atom,
2961 &cpu.barcelona,
2962 &cpu.bdver1,
2963 &cpu.bdver2,
2964 &cpu.bdver3,
2965 &cpu.bdver4,
2966 &cpu.bonnell,
2967 &cpu.broadwell,
2968 &cpu.btver1,
2969 &cpu.btver2,
2970 &cpu.c3,
2971 &cpu.c3_2,
2972 &cpu.cannonlake,
2973 &cpu.cascadelake,
2974 &cpu.cooperlake,
2975 &cpu.core_avx_i,
2976 &cpu.core_avx2,
2977 &cpu.core2,
2978 &cpu.corei7,
2979 &cpu.corei7_avx,
2980 &cpu.generic,
2981 &cpu.geode,
2982 &cpu.goldmont,
2983 &cpu.goldmont_plus,
2984 &cpu.haswell,
2985 &cpu._i386,
2986 &cpu._i486,
2987 &cpu._i586,
2988 &cpu._i686,
2989 &cpu.icelake_client,
2990 &cpu.icelake_server,
2991 &cpu.ivybridge,
2992 &cpu.k6,
2993 &cpu.k6_2,
2994 &cpu.k6_3,
2995 &cpu.k8,
2996 &cpu.k8_sse3,
2997 &cpu.knl,
2998 &cpu.knm,
2999 &cpu.lakemont,
3000 &cpu.nehalem,
3001 &cpu.nocona,
3002 &cpu.opteron,
3003 &cpu.opteron_sse3,
3004 &cpu.penryn,
3005 &cpu.pentium,
3006 &cpu.pentium_m,
3007 &cpu.pentium_mmx,
3008 &cpu.pentium2,
3009 &cpu.pentium3,
3010 &cpu.pentium3m,
3011 &cpu.pentium4,
3012 &cpu.pentium4m,
3013 &cpu.pentiumpro,
3014 &cpu.prescott,
3015 &cpu.sandybridge,
3016 &cpu.silvermont,
3017 &cpu.skx,
3018 &cpu.skylake,
3019 &cpu.skylake_avx512,
3020 &cpu.slm,
3021 &cpu.tigerlake,
3022 &cpu.tremont,
3023 &cpu.westmere,
3024 &cpu.winchip_c6,
3025 &cpu.winchip2,
3026 &cpu.x86_64,
3027 &cpu.yonah,
3028 &cpu.znver1,
3029 &cpu.znver2,
3030};
lib/std/testing.zig+7-4
...@@ -11,12 +11,15 @@ pub var allocator_instance = LeakCountAllocator.init(&base_allocator_instance.al...@@ -11,12 +11,15 @@ pub var allocator_instance = LeakCountAllocator.init(&base_allocator_instance.al
11pub const failing_allocator = &failing_allocator_instance.allocator;11pub const failing_allocator = &failing_allocator_instance.allocator;
12pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);12pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0);
1313
14pub var base_allocator_instance = std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]);14pub var base_allocator_instance = std.mem.validationWrap(std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]));
15var allocator_mem: [2 * 1024 * 1024]u8 = undefined;15var allocator_mem: [2 * 1024 * 1024]u8 = undefined;
1616
17/// TODO https://github.com/ziglang/zig/issues/5738
18pub var log_level = std.log.Level.warn;
19
17/// This function is intended to be used only in tests. It prints diagnostics to stderr20/// This function is intended to be used only in tests. It prints diagnostics to stderr
18/// and then aborts when actual_error_union is not expected_error.21/// and then aborts when actual_error_union is not expected_error.
19pub fn expectError(expected_error: anyerror, actual_error_union: var) void {22pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void {
20 if (actual_error_union) |actual_payload| {23 if (actual_error_union) |actual_payload| {
21 std.debug.panic("expected error.{}, found {}", .{ @errorName(expected_error), actual_payload });24 std.debug.panic("expected error.{}, found {}", .{ @errorName(expected_error), actual_payload });
22 } else |actual_error| {25 } else |actual_error| {
...@@ -33,7 +36,7 @@ pub fn expectError(expected_error: anyerror, actual_error_union: var) void {...@@ -33,7 +36,7 @@ pub fn expectError(expected_error: anyerror, actual_error_union: var) void {
33/// equal, prints diagnostics to stderr to show exactly how they are not equal,36/// equal, prints diagnostics to stderr to show exactly how they are not equal,
34/// then aborts.37/// then aborts.
35/// The types must match exactly.38/// The types must match exactly.
36pub fn expectEqual(expected: var, actual: @TypeOf(expected)) void {39pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void {
37 switch (@typeInfo(@TypeOf(actual))) {40 switch (@typeInfo(@TypeOf(actual))) {
38 .NoReturn,41 .NoReturn,
39 .BoundFn,42 .BoundFn,
...@@ -215,7 +218,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir {...@@ -215,7 +218,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir {
215 defer preopens.deinit();218 defer preopens.deinit();
216 preopens.populate() catch219 preopens.populate() catch
217 @panic("unable to make tmp dir for testing: unable to populate preopens");220 @panic("unable to make tmp dir for testing: unable to populate preopens");
218 const preopen = preopens.find(".") orelse221 const preopen = preopens.find(std.fs.wasi.PreopenType{ .Dir = "." }) orelse
219 @panic("unable to make tmp dir for testing: didn't find '.' in the preopens");222 @panic("unable to make tmp dir for testing: didn't find '.' in the preopens");
220223
221 return std.fs.Dir{ .fd = preopen.fd };224 return std.fs.Dir{ .fd = preopen.fd };
lib/std/testing/failing_allocator.zig+18-23
...@@ -39,43 +39,38 @@ pub const FailingAllocator = struct {...@@ -39,43 +39,38 @@ pub const FailingAllocator = struct {
39 .allocations = 0,39 .allocations = 0,
40 .deallocations = 0,40 .deallocations = 0,
41 .allocator = mem.Allocator{41 .allocator = mem.Allocator{
42 .reallocFn = realloc,42 .allocFn = alloc,
43 .shrinkFn = shrink,43 .resizeFn = resize,
44 },44 },
45 };45 };
46 }46 }
4747
48 fn realloc(allocator: *mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {48 fn alloc(allocator: *std.mem.Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
49 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);49 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
50 if (self.index == self.fail_index) {50 if (self.index == self.fail_index) {
51 return error.OutOfMemory;51 return error.OutOfMemory;
52 }52 }
53 const result = try self.internal_allocator.reallocFn(53 const result = try self.internal_allocator.callAllocFn(len, ptr_align, len_align);
54 self.internal_allocator,54 self.allocated_bytes += result.len;
55 old_mem,55 self.allocations += 1;
56 old_align,
57 new_size,
58 new_align,
59 );
60 if (new_size < old_mem.len) {
61 self.freed_bytes += old_mem.len - new_size;
62 if (new_size == 0)
63 self.deallocations += 1;
64 } else if (new_size > old_mem.len) {
65 self.allocated_bytes += new_size - old_mem.len;
66 if (old_mem.len == 0)
67 self.allocations += 1;
68 }
69 self.index += 1;56 self.index += 1;
70 return result;57 return result;
71 }58 }
7259
73 fn shrink(allocator: *mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {60 fn resize(allocator: *std.mem.Allocator, old_mem: []u8, new_len: usize, len_align: u29) error{OutOfMemory}!usize {
74 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);61 const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
75 const r = self.internal_allocator.shrinkFn(self.internal_allocator, old_mem, old_align, new_size, new_align);62 const r = self.internal_allocator.callResizeFn(old_mem, new_len, len_align) catch |e| {
76 self.freed_bytes += old_mem.len - r.len;63 std.debug.assert(new_len > old_mem.len);
77 if (new_size == 0)64 return e;
65 };
66 if (new_len == 0) {
78 self.deallocations += 1;67 self.deallocations += 1;
68 self.freed_bytes += old_mem.len;
69 } else if (r < old_mem.len) {
70 self.freed_bytes += old_mem.len - r;
71 } else {
72 self.allocated_bytes += r - old_mem.len;
73 }
79 return r;74 return r;
80 }75 }
81};76};
lib/std/testing/leak_count_allocator.zig+11-10
...@@ -14,23 +14,21 @@ pub const LeakCountAllocator = struct {...@@ -14,23 +14,21 @@ pub const LeakCountAllocator = struct {
14 return .{14 return .{
15 .count = 0,15 .count = 0,
16 .allocator = .{16 .allocator = .{
17 .reallocFn = realloc,17 .allocFn = alloc,
18 .shrinkFn = shrink,18 .resizeFn = resize,
19 },19 },
20 .internal_allocator = allocator,20 .internal_allocator = allocator,
21 };21 };
22 }22 }
2323
24 fn realloc(allocator: *std.mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {24 fn alloc(allocator: *std.mem.Allocator, len: usize, ptr_align: u29, len_align: u29) error{OutOfMemory}![]u8 {
25 const self = @fieldParentPtr(LeakCountAllocator, "allocator", allocator);25 const self = @fieldParentPtr(LeakCountAllocator, "allocator", allocator);
26 var data = try self.internal_allocator.reallocFn(self.internal_allocator, old_mem, old_align, new_size, new_align);26 const ptr = try self.internal_allocator.callAllocFn(len, ptr_align, len_align);
27 if (old_mem.len == 0) {27 self.count += 1;
28 self.count += 1;28 return ptr;
29 }
30 return data;
31 }29 }
3230
33 fn shrink(allocator: *std.mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {31 fn resize(allocator: *std.mem.Allocator, old_mem: []u8, new_size: usize, len_align: u29) error{OutOfMemory}!usize {
34 const self = @fieldParentPtr(LeakCountAllocator, "allocator", allocator);32 const self = @fieldParentPtr(LeakCountAllocator, "allocator", allocator);
35 if (new_size == 0) {33 if (new_size == 0) {
36 if (self.count == 0) {34 if (self.count == 0) {
...@@ -38,7 +36,10 @@ pub const LeakCountAllocator = struct {...@@ -38,7 +36,10 @@ pub const LeakCountAllocator = struct {
38 }36 }
39 self.count -= 1;37 self.count -= 1;
40 }38 }
41 return self.internal_allocator.shrinkFn(self.internal_allocator, old_mem, old_align, new_size, new_align);39 return self.internal_allocator.callResizeFn(old_mem, new_size, len_align) catch |e| {
40 std.debug.assert(new_size > old_mem.len);
41 return e;
42 };
42 }43 }
4344
44 pub fn validate(self: LeakCountAllocator) !void {45 pub fn validate(self: LeakCountAllocator) !void {
lib/std/thread.zig+1-1
...@@ -143,7 +143,7 @@ pub const Thread = struct {...@@ -143,7 +143,7 @@ pub const Thread = struct {
143 /// fn startFn(@TypeOf(context)) T143 /// fn startFn(@TypeOf(context)) T
144 /// where T is u8, noreturn, void, or !void144 /// where T is u8, noreturn, void, or !void
145 /// caller must call wait on the returned thread145 /// caller must call wait on the returned thread
146 pub fn spawn(context: var, comptime startFn: var) SpawnError!*Thread {146 pub fn spawn(context: anytype, comptime startFn: anytype) SpawnError!*Thread {
147 if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");147 if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");
148 // TODO compile-time call graph analysis to determine stack upper bound148 // TODO compile-time call graph analysis to determine stack upper bound
149 // https://github.com/ziglang/zig/issues/157149 // https://github.com/ziglang/zig/issues/157
lib/std/unicode.zig+41
...@@ -235,6 +235,22 @@ pub const Utf8Iterator = struct {...@@ -235,6 +235,22 @@ pub const Utf8Iterator = struct {
235 else => unreachable,235 else => unreachable,
236 }236 }
237 }237 }
238
239 /// Look ahead at the next n codepoints without advancing the iterator.
240 /// If fewer than n codepoints are available, then return the remainder of the string.
241 pub fn peek(it: *Utf8Iterator, n: usize) []const u8 {
242 const original_i = it.i;
243 defer it.i = original_i;
244
245 var end_ix = original_i;
246 var found: usize = 0;
247 while (found < n) : (found += 1) {
248 const next_codepoint = it.nextCodepointSlice() orelse return it.bytes[original_i..];
249 end_ix += next_codepoint.len;
250 }
251
252 return it.bytes[original_i..end_ix];
253 }
238};254};
239255
240pub const Utf16LeIterator = struct {256pub const Utf16LeIterator = struct {
...@@ -451,6 +467,31 @@ fn testMiscInvalidUtf8() void {...@@ -451,6 +467,31 @@ fn testMiscInvalidUtf8() void {
451 testValid("\xee\x80\x80", 0xe000);467 testValid("\xee\x80\x80", 0xe000);
452}468}
453469
470test "utf8 iterator peeking" {
471 comptime testUtf8Peeking();
472 testUtf8Peeking();
473}
474
475fn testUtf8Peeking() void {
476 const s = Utf8View.initComptime("noël");
477 var it = s.iterator();
478
479 testing.expect(std.mem.eql(u8, "n", it.nextCodepointSlice().?));
480
481 testing.expect(std.mem.eql(u8, "o", it.peek(1)));
482 testing.expect(std.mem.eql(u8, "oë", it.peek(2)));
483 testing.expect(std.mem.eql(u8, "oël", it.peek(3)));
484 testing.expect(std.mem.eql(u8, "oël", it.peek(4)));
485 testing.expect(std.mem.eql(u8, "oël", it.peek(10)));
486
487 testing.expect(std.mem.eql(u8, "o", it.nextCodepointSlice().?));
488 testing.expect(std.mem.eql(u8, "ë", it.nextCodepointSlice().?));
489 testing.expect(std.mem.eql(u8, "l", it.nextCodepointSlice().?));
490 testing.expect(it.nextCodepointSlice() == null);
491
492 testing.expect(std.mem.eql(u8, &[_]u8{}, it.peek(1)));
493}
494
454fn testError(bytes: []const u8, expected_err: anyerror) void {495fn testError(bytes: []const u8, expected_err: anyerror) void {
455 testing.expectError(expected_err, testDecode(bytes));496 testing.expectError(expected_err, testDecode(bytes));
456}497}
lib/std/zig.zig+38
...@@ -1,4 +1,6 @@...@@ -1,4 +1,6 @@
1const std = @import("std.zig");
1const tokenizer = @import("zig/tokenizer.zig");2const tokenizer = @import("zig/tokenizer.zig");
3
2pub const Token = tokenizer.Token;4pub const Token = tokenizer.Token;
3pub const Tokenizer = tokenizer.Tokenizer;5pub const Tokenizer = tokenizer.Tokenizer;
4pub const parse = @import("zig/parse.zig").parse;6pub const parse = @import("zig/parse.zig").parse;
...@@ -9,6 +11,21 @@ pub const ast = @import("zig/ast.zig");...@@ -9,6 +11,21 @@ pub const ast = @import("zig/ast.zig");
9pub const system = @import("zig/system.zig");11pub const system = @import("zig/system.zig");
10pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;12pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
1113
14pub const SrcHash = [16]u8;
15
16/// If the source is small enough, it is used directly as the hash.
17/// If it is long, blake3 hash is computed.
18pub fn hashSrc(src: []const u8) SrcHash {
19 var out: SrcHash = undefined;
20 if (src.len <= SrcHash.len) {
21 std.mem.copy(u8, &out, src);
22 std.mem.set(u8, out[src.len..], 0);
23 } else {
24 std.crypto.Blake3.hash(src, &out);
25 }
26 return out;
27}
28
12pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {29pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
13 var line: usize = 0;30 var line: usize = 0;
14 var column: usize = 0;31 var column: usize = 0;
...@@ -26,6 +43,27 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi...@@ -26,6 +43,27 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi
26 return .{ .line = line, .column = column };43 return .{ .line = line, .column = column };
27}44}
2845
46/// Returns the standard file system basename of a binary generated by the Zig compiler.
47pub fn binNameAlloc(
48 allocator: *std.mem.Allocator,
49 root_name: []const u8,
50 target: std.Target,
51 output_mode: std.builtin.OutputMode,
52 link_mode: ?std.builtin.LinkMode,
53) error{OutOfMemory}![]u8 {
54 switch (output_mode) {
55 .Exe => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.exeFileExt() }),
56 .Lib => {
57 const suffix = switch (link_mode orelse .Static) {
58 .Static => target.staticLibSuffix(),
59 .Dynamic => target.dynamicLibSuffix(),
60 };
61 return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix });
62 },
63 .Obj => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.oFileExt() }),
64 }
65}
66
29test "" {67test "" {
30 @import("std").meta.refAllDecls(@This());68 @import("std").meta.refAllDecls(@This());
31}69}
lib/std/zig/ast.zig+639-342
...@@ -29,7 +29,7 @@ pub const Tree = struct {...@@ -29,7 +29,7 @@ pub const Tree = struct {
29 self.arena.promote(self.gpa).deinit();29 self.arena.promote(self.gpa).deinit();
30 }30 }
3131
32 pub fn renderError(self: *Tree, parse_error: *const Error, stream: var) !void {32 pub fn renderError(self: *Tree, parse_error: *const Error, stream: anytype) !void {
33 return parse_error.render(self.token_ids, stream);33 return parse_error.render(self.token_ids, stream);
34 }34 }
3535
...@@ -167,7 +167,7 @@ pub const Error = union(enum) {...@@ -167,7 +167,7 @@ pub const Error = union(enum) {
167 DeclBetweenFields: DeclBetweenFields,167 DeclBetweenFields: DeclBetweenFields,
168 InvalidAnd: InvalidAnd,168 InvalidAnd: InvalidAnd,
169169
170 pub fn render(self: *const Error, tokens: []const Token.Id, stream: var) !void {170 pub fn render(self: *const Error, tokens: []const Token.Id, stream: anytype) !void {
171 switch (self.*) {171 switch (self.*) {
172 .InvalidToken => |*x| return x.render(tokens, stream),172 .InvalidToken => |*x| return x.render(tokens, stream),
173 .ExpectedContainerMembers => |*x| return x.render(tokens, stream),173 .ExpectedContainerMembers => |*x| return x.render(tokens, stream),
...@@ -322,9 +322,9 @@ pub const Error = union(enum) {...@@ -322,9 +322,9 @@ pub const Error = union(enum) {
322 pub const ExpectedCall = struct {322 pub const ExpectedCall = struct {
323 node: *Node,323 node: *Node,
324324
325 pub fn render(self: *const ExpectedCall, tokens: []const Token.Id, stream: var) !void {325 pub fn render(self: *const ExpectedCall, tokens: []const Token.Id, stream: anytype) !void {
326 return stream.print("expected " ++ @tagName(Node.Id.Call) ++ ", found {}", .{326 return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ ", found {}", .{
327 @tagName(self.node.id),327 @tagName(self.node.tag),
328 });328 });
329 }329 }
330 };330 };
...@@ -332,9 +332,9 @@ pub const Error = union(enum) {...@@ -332,9 +332,9 @@ pub const Error = union(enum) {
332 pub const ExpectedCallOrFnProto = struct {332 pub const ExpectedCallOrFnProto = struct {
333 node: *Node,333 node: *Node,
334334
335 pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token.Id, stream: var) !void {335 pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token.Id, stream: anytype) !void {
336 return stream.print("expected " ++ @tagName(Node.Id.Call) ++ " or " ++336 return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ " or " ++
337 @tagName(Node.Id.FnProto) ++ ", found {}", .{@tagName(self.node.id)});337 @tagName(Node.Tag.FnProto) ++ ", found {}", .{@tagName(self.node.tag)});
338 }338 }
339 };339 };
340340
...@@ -342,7 +342,7 @@ pub const Error = union(enum) {...@@ -342,7 +342,7 @@ pub const Error = union(enum) {
342 token: TokenIndex,342 token: TokenIndex,
343 expected_id: Token.Id,343 expected_id: Token.Id,
344344
345 pub fn render(self: *const ExpectedToken, tokens: []const Token.Id, stream: var) !void {345 pub fn render(self: *const ExpectedToken, tokens: []const Token.Id, stream: anytype) !void {
346 const found_token = tokens[self.token];346 const found_token = tokens[self.token];
347 switch (found_token) {347 switch (found_token) {
348 .Invalid => {348 .Invalid => {
...@@ -360,7 +360,7 @@ pub const Error = union(enum) {...@@ -360,7 +360,7 @@ pub const Error = union(enum) {
360 token: TokenIndex,360 token: TokenIndex,
361 end_id: Token.Id,361 end_id: Token.Id,
362362
363 pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Id, stream: var) !void {363 pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Id, stream: anytype) !void {
364 const actual_token = tokens[self.token];364 const actual_token = tokens[self.token];
365 return stream.print("expected ',' or '{}', found '{}'", .{365 return stream.print("expected ',' or '{}', found '{}'", .{
366 self.end_id.symbol(),366 self.end_id.symbol(),
...@@ -375,7 +375,7 @@ pub const Error = union(enum) {...@@ -375,7 +375,7 @@ pub const Error = union(enum) {
375375
376 token: TokenIndex,376 token: TokenIndex,
377377
378 pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: var) !void {378 pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: anytype) !void {
379 const actual_token = tokens[self.token];379 const actual_token = tokens[self.token];
380 return stream.print(msg, .{actual_token.symbol()});380 return stream.print(msg, .{actual_token.symbol()});
381 }381 }
...@@ -388,7 +388,7 @@ pub const Error = union(enum) {...@@ -388,7 +388,7 @@ pub const Error = union(enum) {
388388
389 token: TokenIndex,389 token: TokenIndex,
390390
391 pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: var) !void {391 pub fn render(self: *const ThisError, tokens: []const Token.Id, stream: anytype) !void {
392 return stream.writeAll(msg);392 return stream.writeAll(msg);
393 }393 }
394 };394 };
...@@ -396,9 +396,9 @@ pub const Error = union(enum) {...@@ -396,9 +396,9 @@ pub const Error = union(enum) {
396};396};
397397
398pub const Node = struct {398pub const Node = struct {
399 id: Id,399 tag: Tag,
400400
401 pub const Id = enum {401 pub const Tag = enum {
402 // Top level402 // Top level
403 Root,403 Root,
404 Use,404 Use,
...@@ -408,9 +408,69 @@ pub const Node = struct {...@@ -408,9 +408,69 @@ pub const Node = struct {
408 VarDecl,408 VarDecl,
409 Defer,409 Defer,
410410
411 // Operators411 // Infix operators
412 InfixOp,412 Catch,
413 PrefixOp,413
414 // SimpleInfixOp
415 Add,
416 AddWrap,
417 ArrayCat,
418 ArrayMult,
419 Assign,
420 AssignBitAnd,
421 AssignBitOr,
422 AssignBitShiftLeft,
423 AssignBitShiftRight,
424 AssignBitXor,
425 AssignDiv,
426 AssignSub,
427 AssignSubWrap,
428 AssignMod,
429 AssignAdd,
430 AssignAddWrap,
431 AssignMul,
432 AssignMulWrap,
433 BangEqual,
434 BitAnd,
435 BitOr,
436 BitShiftLeft,
437 BitShiftRight,
438 BitXor,
439 BoolAnd,
440 BoolOr,
441 Div,
442 EqualEqual,
443 ErrorUnion,
444 GreaterOrEqual,
445 GreaterThan,
446 LessOrEqual,
447 LessThan,
448 MergeErrorSets,
449 Mod,
450 Mul,
451 MulWrap,
452 Period,
453 Range,
454 Sub,
455 SubWrap,
456 UnwrapOptional,
457
458 // SimplePrefixOp
459 AddressOf,
460 Await,
461 BitNot,
462 BoolNot,
463 OptionalType,
464 Negation,
465 NegationWrap,
466 Resume,
467 Try,
468
469 ArrayType,
470 /// ArrayType but has a sentinel node.
471 ArrayTypeSentinel,
472 PtrType,
473 SliceType,
414 /// Not all suffix operations are under this tag. To save memory, some474 /// Not all suffix operations are under this tag. To save memory, some
415 /// suffix operations have dedicated Node tags.475 /// suffix operations have dedicated Node tags.
416 SuffixOp,476 SuffixOp,
...@@ -434,7 +494,7 @@ pub const Node = struct {...@@ -434,7 +494,7 @@ pub const Node = struct {
434 Suspend,494 Suspend,
435495
436 // Type expressions496 // Type expressions
437 VarType,497 AnyType,
438 ErrorType,498 ErrorType,
439 FnProto,499 FnProto,
440 AnyFrameType,500 AnyFrameType,
...@@ -471,49 +531,177 @@ pub const Node = struct {...@@ -471,49 +531,177 @@ pub const Node = struct {
471 ContainerField,531 ContainerField,
472 ErrorTag,532 ErrorTag,
473 FieldInitializer,533 FieldInitializer,
534
535 pub fn Type(tag: Tag) type {
536 return switch (tag) {
537 .Root => Root,
538 .Use => Use,
539 .TestDecl => TestDecl,
540 .VarDecl => VarDecl,
541 .Defer => Defer,
542 .Catch => Catch,
543
544 .Add,
545 .AddWrap,
546 .ArrayCat,
547 .ArrayMult,
548 .Assign,
549 .AssignBitAnd,
550 .AssignBitOr,
551 .AssignBitShiftLeft,
552 .AssignBitShiftRight,
553 .AssignBitXor,
554 .AssignDiv,
555 .AssignSub,
556 .AssignSubWrap,
557 .AssignMod,
558 .AssignAdd,
559 .AssignAddWrap,
560 .AssignMul,
561 .AssignMulWrap,
562 .BangEqual,
563 .BitAnd,
564 .BitOr,
565 .BitShiftLeft,
566 .BitShiftRight,
567 .BitXor,
568 .BoolAnd,
569 .BoolOr,
570 .Div,
571 .EqualEqual,
572 .ErrorUnion,
573 .GreaterOrEqual,
574 .GreaterThan,
575 .LessOrEqual,
576 .LessThan,
577 .MergeErrorSets,
578 .Mod,
579 .Mul,
580 .MulWrap,
581 .Period,
582 .Range,
583 .Sub,
584 .SubWrap,
585 .UnwrapOptional,
586 => SimpleInfixOp,
587
588 .AddressOf,
589 .Await,
590 .BitNot,
591 .BoolNot,
592 .OptionalType,
593 .Negation,
594 .NegationWrap,
595 .Resume,
596 .Try,
597 => SimplePrefixOp,
598
599 .ArrayType => ArrayType,
600 .ArrayTypeSentinel => ArrayTypeSentinel,
601
602 .PtrType => PtrType,
603 .SliceType => SliceType,
604 .SuffixOp => SuffixOp,
605
606 .ArrayInitializer => ArrayInitializer,
607 .ArrayInitializerDot => ArrayInitializerDot,
608
609 .StructInitializer => StructInitializer,
610 .StructInitializerDot => StructInitializerDot,
611
612 .Call => Call,
613 .Switch => Switch,
614 .While => While,
615 .For => For,
616 .If => If,
617 .ControlFlowExpression => ControlFlowExpression,
618 .Suspend => Suspend,
619 .AnyType => AnyType,
620 .ErrorType => ErrorType,
621 .FnProto => FnProto,
622 .AnyFrameType => AnyFrameType,
623 .IntegerLiteral => IntegerLiteral,
624 .FloatLiteral => FloatLiteral,
625 .EnumLiteral => EnumLiteral,
626 .StringLiteral => StringLiteral,
627 .MultilineStringLiteral => MultilineStringLiteral,
628 .CharLiteral => CharLiteral,
629 .BoolLiteral => BoolLiteral,
630 .NullLiteral => NullLiteral,
631 .UndefinedLiteral => UndefinedLiteral,
632 .Unreachable => Unreachable,
633 .Identifier => Identifier,
634 .GroupedExpression => GroupedExpression,
635 .BuiltinCall => BuiltinCall,
636 .ErrorSetDecl => ErrorSetDecl,
637 .ContainerDecl => ContainerDecl,
638 .Asm => Asm,
639 .Comptime => Comptime,
640 .Nosuspend => Nosuspend,
641 .Block => Block,
642 .DocComment => DocComment,
643 .SwitchCase => SwitchCase,
644 .SwitchElse => SwitchElse,
645 .Else => Else,
646 .Payload => Payload,
647 .PointerPayload => PointerPayload,
648 .PointerIndexPayload => PointerIndexPayload,
649 .ContainerField => ContainerField,
650 .ErrorTag => ErrorTag,
651 .FieldInitializer => FieldInitializer,
652 };
653 }
474 };654 };
475655
656 /// Prefer `castTag` to this.
476 pub fn cast(base: *Node, comptime T: type) ?*T {657 pub fn cast(base: *Node, comptime T: type) ?*T {
477 if (base.id == comptime typeToId(T)) {658 if (std.meta.fieldInfo(T, "base").default_value) |default_base| {
478 return @fieldParentPtr(T, "base", base);659 return base.castTag(default_base.tag);
660 }
661 inline for (@typeInfo(Tag).Enum.fields) |field| {
662 const tag = @intToEnum(Tag, field.value);
663 if (base.tag == tag) {
664 if (T == tag.Type()) {
665 return @fieldParentPtr(T, "base", base);
666 }
667 return null;
668 }
669 }
670 unreachable;
671 }
672
673 pub fn castTag(base: *Node, comptime tag: Tag) ?*tag.Type() {
674 if (base.tag == tag) {
675 return @fieldParentPtr(tag.Type(), "base", base);
479 }676 }
480 return null;677 return null;
481 }678 }
482679
483 pub fn iterate(base: *Node, index: usize) ?*Node {680 pub fn iterate(base: *Node, index: usize) ?*Node {
484 inline for (@typeInfo(Id).Enum.fields) |f| {681 inline for (@typeInfo(Tag).Enum.fields) |field| {
485 if (base.id == @field(Id, f.name)) {682 const tag = @intToEnum(Tag, field.value);
486 const T = @field(Node, f.name);683 if (base.tag == tag) {
487 return @fieldParentPtr(T, "base", base).iterate(index);684 return @fieldParentPtr(tag.Type(), "base", base).iterate(index);
488 }685 }
489 }686 }
490 unreachable;687 unreachable;
491 }688 }
492689
493 pub fn firstToken(base: *const Node) TokenIndex {690 pub fn firstToken(base: *const Node) TokenIndex {
494 inline for (@typeInfo(Id).Enum.fields) |f| {691 inline for (@typeInfo(Tag).Enum.fields) |field| {
495 if (base.id == @field(Id, f.name)) {692 const tag = @intToEnum(Tag, field.value);
496 const T = @field(Node, f.name);693 if (base.tag == tag) {
497 return @fieldParentPtr(T, "base", base).firstToken();694 return @fieldParentPtr(tag.Type(), "base", base).firstToken();
498 }695 }
499 }696 }
500 unreachable;697 unreachable;
501 }698 }
502699
503 pub fn lastToken(base: *const Node) TokenIndex {700 pub fn lastToken(base: *const Node) TokenIndex {
504 inline for (@typeInfo(Id).Enum.fields) |f| {701 inline for (@typeInfo(Tag).Enum.fields) |field| {
505 if (base.id == @field(Id, f.name)) {702 const tag = @intToEnum(Tag, field.value);
506 const T = @field(Node, f.name);703 if (base.tag == tag) {
507 return @fieldParentPtr(T, "base", base).lastToken();704 return @fieldParentPtr(tag.Type(), "base", base).lastToken();
508 }
509 }
510 unreachable;
511 }
512
513 pub fn typeToId(comptime T: type) Id {
514 inline for (@typeInfo(Id).Enum.fields) |f| {
515 if (T == @field(Node, f.name)) {
516 return @field(Id, f.name);
517 }705 }
518 }706 }
519 unreachable;707 unreachable;
...@@ -522,7 +710,7 @@ pub const Node = struct {...@@ -522,7 +710,7 @@ pub const Node = struct {
522 pub fn requireSemiColon(base: *const Node) bool {710 pub fn requireSemiColon(base: *const Node) bool {
523 var n = base;711 var n = base;
524 while (true) {712 while (true) {
525 switch (n.id) {713 switch (n.tag) {
526 .Root,714 .Root,
527 .ContainerField,715 .ContainerField,
528 .Block,716 .Block,
...@@ -543,7 +731,7 @@ pub const Node = struct {...@@ -543,7 +731,7 @@ pub const Node = struct {
543 continue;731 continue;
544 }732 }
545733
546 return while_node.body.id != .Block;734 return while_node.body.tag != .Block;
547 },735 },
548 .For => {736 .For => {
549 const for_node = @fieldParentPtr(For, "base", n);737 const for_node = @fieldParentPtr(For, "base", n);
...@@ -552,7 +740,7 @@ pub const Node = struct {...@@ -552,7 +740,7 @@ pub const Node = struct {
552 continue;740 continue;
553 }741 }
554742
555 return for_node.body.id != .Block;743 return for_node.body.tag != .Block;
556 },744 },
557 .If => {745 .If => {
558 const if_node = @fieldParentPtr(If, "base", n);746 const if_node = @fieldParentPtr(If, "base", n);
...@@ -561,7 +749,7 @@ pub const Node = struct {...@@ -561,7 +749,7 @@ pub const Node = struct {
561 continue;749 continue;
562 }750 }
563751
564 return if_node.body.id != .Block;752 return if_node.body.tag != .Block;
565 },753 },
566 .Else => {754 .Else => {
567 const else_node = @fieldParentPtr(Else, "base", n);755 const else_node = @fieldParentPtr(Else, "base", n);
...@@ -570,23 +758,23 @@ pub const Node = struct {...@@ -570,23 +758,23 @@ pub const Node = struct {
570 },758 },
571 .Defer => {759 .Defer => {
572 const defer_node = @fieldParentPtr(Defer, "base", n);760 const defer_node = @fieldParentPtr(Defer, "base", n);
573 return defer_node.expr.id != .Block;761 return defer_node.expr.tag != .Block;
574 },762 },
575 .Comptime => {763 .Comptime => {
576 const comptime_node = @fieldParentPtr(Comptime, "base", n);764 const comptime_node = @fieldParentPtr(Comptime, "base", n);
577 return comptime_node.expr.id != .Block;765 return comptime_node.expr.tag != .Block;
578 },766 },
579 .Suspend => {767 .Suspend => {
580 const suspend_node = @fieldParentPtr(Suspend, "base", n);768 const suspend_node = @fieldParentPtr(Suspend, "base", n);
581 if (suspend_node.body) |body| {769 if (suspend_node.body) |body| {
582 return body.id != .Block;770 return body.tag != .Block;
583 }771 }
584772
585 return true;773 return true;
586 },774 },
587 .Nosuspend => {775 .Nosuspend => {
588 const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n);776 const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n);
589 return nosuspend_node.expr.id != .Block;777 return nosuspend_node.expr.tag != .Block;
590 },778 },
591 else => return true,779 else => return true,
592 }780 }
...@@ -600,7 +788,7 @@ pub const Node = struct {...@@ -600,7 +788,7 @@ pub const Node = struct {
600 std.debug.warn(" ", .{});788 std.debug.warn(" ", .{});
601 }789 }
602 }790 }
603 std.debug.warn("{}\n", .{@tagName(self.id)});791 std.debug.warn("{}\n", .{@tagName(self.tag)});
604792
605 var child_i: usize = 0;793 var child_i: usize = 0;
606 while (self.iterate(child_i)) |child| : (child_i += 1) {794 while (self.iterate(child_i)) |child| : (child_i += 1) {
...@@ -610,7 +798,7 @@ pub const Node = struct {...@@ -610,7 +798,7 @@ pub const Node = struct {
610798
611 /// The decls data follows this struct in memory as an array of Node pointers.799 /// The decls data follows this struct in memory as an array of Node pointers.
612 pub const Root = struct {800 pub const Root = struct {
613 base: Node = Node{ .id = .Root },801 base: Node = Node{ .tag = .Root },
614 eof_token: TokenIndex,802 eof_token: TokenIndex,
615 decls_len: NodeIndex,803 decls_len: NodeIndex,
616804
...@@ -662,42 +850,84 @@ pub const Node = struct {...@@ -662,42 +850,84 @@ pub const Node = struct {
662 }850 }
663 };851 };
664852
853 /// Trailed in memory by possibly many things, with each optional thing
854 /// determined by a bit in `trailer_flags`.
665 pub const VarDecl = struct {855 pub const VarDecl = struct {
666 base: Node = Node{ .id = .VarDecl },856 base: Node = Node{ .tag = .VarDecl },
667 doc_comments: ?*DocComment,857 trailer_flags: TrailerFlags,
668 visib_token: ?TokenIndex,
669 thread_local_token: ?TokenIndex,
670 name_token: TokenIndex,
671 eq_token: ?TokenIndex,
672 mut_token: TokenIndex,858 mut_token: TokenIndex,
673 comptime_token: ?TokenIndex,859 name_token: TokenIndex,
674 extern_export_token: ?TokenIndex,
675 lib_name: ?*Node,
676 type_node: ?*Node,
677 align_node: ?*Node,
678 section_node: ?*Node,
679 init_node: ?*Node,
680 semicolon_token: TokenIndex,860 semicolon_token: TokenIndex,
681861
862 pub const TrailerFlags = std.meta.TrailerFlags(struct {
863 doc_comments: *DocComment,
864 visib_token: TokenIndex,
865 thread_local_token: TokenIndex,
866 eq_token: TokenIndex,
867 comptime_token: TokenIndex,
868 extern_export_token: TokenIndex,
869 lib_name: *Node,
870 type_node: *Node,
871 align_node: *Node,
872 section_node: *Node,
873 init_node: *Node,
874 });
875
876 pub const RequiredFields = struct {
877 mut_token: TokenIndex,
878 name_token: TokenIndex,
879 semicolon_token: TokenIndex,
880 };
881
882 pub fn getTrailer(self: *const VarDecl, comptime name: []const u8) ?TrailerFlags.Field(name) {
883 const trailers_start = @ptrCast([*]const u8, self) + @sizeOf(VarDecl);
884 return self.trailer_flags.get(trailers_start, name);
885 }
886
887 pub fn setTrailer(self: *VarDecl, comptime name: []const u8, value: TrailerFlags.Field(name)) void {
888 const trailers_start = @ptrCast([*]u8, self) + @sizeOf(VarDecl);
889 self.trailer_flags.set(trailers_start, name, value);
890 }
891
892 pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: anytype) !*VarDecl {
893 const trailer_flags = TrailerFlags.init(trailers);
894 const bytes = try allocator.alignedAlloc(u8, @alignOf(VarDecl), sizeInBytes(trailer_flags));
895 const var_decl = @ptrCast(*VarDecl, bytes.ptr);
896 var_decl.* = .{
897 .trailer_flags = trailer_flags,
898 .mut_token = required.mut_token,
899 .name_token = required.name_token,
900 .semicolon_token = required.semicolon_token,
901 };
902 const trailers_start = bytes.ptr + @sizeOf(VarDecl);
903 trailer_flags.setMany(trailers_start, trailers);
904 return var_decl;
905 }
906
907 pub fn destroy(self: *VarDecl, allocator: *mem.Allocator) void {
908 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.trailer_flags)];
909 allocator.free(bytes);
910 }
911
682 pub fn iterate(self: *const VarDecl, index: usize) ?*Node {912 pub fn iterate(self: *const VarDecl, index: usize) ?*Node {
683 var i = index;913 var i = index;
684914
685 if (self.type_node) |type_node| {915 if (self.getTrailer("type_node")) |type_node| {
686 if (i < 1) return type_node;916 if (i < 1) return type_node;
687 i -= 1;917 i -= 1;
688 }918 }
689919
690 if (self.align_node) |align_node| {920 if (self.getTrailer("align_node")) |align_node| {
691 if (i < 1) return align_node;921 if (i < 1) return align_node;
692 i -= 1;922 i -= 1;
693 }923 }
694924
695 if (self.section_node) |section_node| {925 if (self.getTrailer("section_node")) |section_node| {
696 if (i < 1) return section_node;926 if (i < 1) return section_node;
697 i -= 1;927 i -= 1;
698 }928 }
699929
700 if (self.init_node) |init_node| {930 if (self.getTrailer("init_node")) |init_node| {
701 if (i < 1) return init_node;931 if (i < 1) return init_node;
702 i -= 1;932 i -= 1;
703 }933 }
...@@ -706,21 +936,25 @@ pub const Node = struct {...@@ -706,21 +936,25 @@ pub const Node = struct {
706 }936 }
707937
708 pub fn firstToken(self: *const VarDecl) TokenIndex {938 pub fn firstToken(self: *const VarDecl) TokenIndex {
709 if (self.visib_token) |visib_token| return visib_token;939 if (self.getTrailer("visib_token")) |visib_token| return visib_token;
710 if (self.thread_local_token) |thread_local_token| return thread_local_token;940 if (self.getTrailer("thread_local_token")) |thread_local_token| return thread_local_token;
711 if (self.comptime_token) |comptime_token| return comptime_token;941 if (self.getTrailer("comptime_token")) |comptime_token| return comptime_token;
712 if (self.extern_export_token) |extern_export_token| return extern_export_token;942 if (self.getTrailer("extern_export_token")) |extern_export_token| return extern_export_token;
713 assert(self.lib_name == null);943 assert(self.getTrailer("lib_name") == null);
714 return self.mut_token;944 return self.mut_token;
715 }945 }
716946
717 pub fn lastToken(self: *const VarDecl) TokenIndex {947 pub fn lastToken(self: *const VarDecl) TokenIndex {
718 return self.semicolon_token;948 return self.semicolon_token;
719 }949 }
950
951 fn sizeInBytes(trailer_flags: TrailerFlags) usize {
952 return @sizeOf(VarDecl) + trailer_flags.sizeInBytes();
953 }
720 };954 };
721955
722 pub const Use = struct {956 pub const Use = struct {
723 base: Node = Node{ .id = .Use },957 base: Node = Node{ .tag = .Use },
724 doc_comments: ?*DocComment,958 doc_comments: ?*DocComment,
725 visib_token: ?TokenIndex,959 visib_token: ?TokenIndex,
726 use_token: TokenIndex,960 use_token: TokenIndex,
...@@ -747,7 +981,7 @@ pub const Node = struct {...@@ -747,7 +981,7 @@ pub const Node = struct {
747 };981 };
748982
749 pub const ErrorSetDecl = struct {983 pub const ErrorSetDecl = struct {
750 base: Node = Node{ .id = .ErrorSetDecl },984 base: Node = Node{ .tag = .ErrorSetDecl },
751 error_token: TokenIndex,985 error_token: TokenIndex,
752 rbrace_token: TokenIndex,986 rbrace_token: TokenIndex,
753 decls_len: NodeIndex,987 decls_len: NodeIndex,
...@@ -797,7 +1031,7 @@ pub const Node = struct {...@@ -797,7 +1031,7 @@ pub const Node = struct {
7971031
798 /// The fields and decls Node pointers directly follow this struct in memory.1032 /// The fields and decls Node pointers directly follow this struct in memory.
799 pub const ContainerDecl = struct {1033 pub const ContainerDecl = struct {
800 base: Node = Node{ .id = .ContainerDecl },1034 base: Node = Node{ .tag = .ContainerDecl },
801 kind_token: TokenIndex,1035 kind_token: TokenIndex,
802 layout_token: ?TokenIndex,1036 layout_token: ?TokenIndex,
803 lbrace_token: TokenIndex,1037 lbrace_token: TokenIndex,
...@@ -866,7 +1100,7 @@ pub const Node = struct {...@@ -866,7 +1100,7 @@ pub const Node = struct {
866 };1100 };
8671101
868 pub const ContainerField = struct {1102 pub const ContainerField = struct {
869 base: Node = Node{ .id = .ContainerField },1103 base: Node = Node{ .tag = .ContainerField },
870 doc_comments: ?*DocComment,1104 doc_comments: ?*DocComment,
871 comptime_token: ?TokenIndex,1105 comptime_token: ?TokenIndex,
872 name_token: TokenIndex,1106 name_token: TokenIndex,
...@@ -917,7 +1151,7 @@ pub const Node = struct {...@@ -917,7 +1151,7 @@ pub const Node = struct {
917 };1151 };
9181152
919 pub const ErrorTag = struct {1153 pub const ErrorTag = struct {
920 base: Node = Node{ .id = .ErrorTag },1154 base: Node = Node{ .tag = .ErrorTag },
921 doc_comments: ?*DocComment,1155 doc_comments: ?*DocComment,
922 name_token: TokenIndex,1156 name_token: TokenIndex,
9231157
...@@ -942,7 +1176,7 @@ pub const Node = struct {...@@ -942,7 +1176,7 @@ pub const Node = struct {
942 };1176 };
9431177
944 pub const Identifier = struct {1178 pub const Identifier = struct {
945 base: Node = Node{ .id = .Identifier },1179 base: Node = Node{ .tag = .Identifier },
946 token: TokenIndex,1180 token: TokenIndex,
9471181
948 pub fn iterate(self: *const Identifier, index: usize) ?*Node {1182 pub fn iterate(self: *const Identifier, index: usize) ?*Node {
...@@ -959,23 +1193,34 @@ pub const Node = struct {...@@ -959,23 +1193,34 @@ pub const Node = struct {
959 };1193 };
9601194
961 /// The params are directly after the FnProto in memory.1195 /// The params are directly after the FnProto in memory.
1196 /// Next, each optional thing determined by a bit in `trailer_flags`.
962 pub const FnProto = struct {1197 pub const FnProto = struct {
963 base: Node = Node{ .id = .FnProto },1198 base: Node = Node{ .tag = .FnProto },
964 doc_comments: ?*DocComment,1199 trailer_flags: TrailerFlags,
965 visib_token: ?TokenIndex,
966 fn_token: TokenIndex,1200 fn_token: TokenIndex,
967 name_token: ?TokenIndex,
968 params_len: NodeIndex,1201 params_len: NodeIndex,
969 return_type: ReturnType,1202 return_type: ReturnType,
970 var_args_token: ?TokenIndex,1203
971 extern_export_inline_token: ?TokenIndex,1204 pub const TrailerFlags = std.meta.TrailerFlags(struct {
972 body_node: ?*Node,1205 doc_comments: *DocComment,
973 lib_name: ?*Node, // populated if this is an extern declaration1206 body_node: *Node,
974 align_expr: ?*Node, // populated if align(A) is present1207 lib_name: *Node, // populated if this is an extern declaration
975 section_expr: ?*Node, // populated if linksection(A) is present1208 align_expr: *Node, // populated if align(A) is present
976 callconv_expr: ?*Node, // populated if callconv(A) is present1209 section_expr: *Node, // populated if linksection(A) is present
977 is_extern_prototype: bool = false, // TODO: Remove once extern fn rewriting is1210 callconv_expr: *Node, // populated if callconv(A) is present
978 is_async: bool = false, // TODO: remove once async fn rewriting is1211 visib_token: TokenIndex,
1212 name_token: TokenIndex,
1213 var_args_token: TokenIndex,
1214 extern_export_inline_token: TokenIndex,
1215 is_extern_prototype: void, // TODO: Remove once extern fn rewriting is
1216 is_async: void, // TODO: remove once async fn rewriting is
1217 });
1218
1219 pub const RequiredFields = struct {
1220 fn_token: TokenIndex,
1221 params_len: NodeIndex,
1222 return_type: ReturnType,
1223 };
9791224
980 pub const ReturnType = union(enum) {1225 pub const ReturnType = union(enum) {
981 Explicit: *Node,1226 Explicit: *Node,
...@@ -991,8 +1236,7 @@ pub const Node = struct {...@@ -991,8 +1236,7 @@ pub const Node = struct {
991 param_type: ParamType,1236 param_type: ParamType,
9921237
993 pub const ParamType = union(enum) {1238 pub const ParamType = union(enum) {
994 var_type: *Node,1239 any_type: *Node,
995 var_args: TokenIndex,
996 type_expr: *Node,1240 type_expr: *Node,
997 };1241 };
9981242
...@@ -1001,8 +1245,7 @@ pub const Node = struct {...@@ -1001,8 +1245,7 @@ pub const Node = struct {
10011245
1002 if (i < 1) {1246 if (i < 1) {
1003 switch (self.param_type) {1247 switch (self.param_type) {
1004 .var_args => return null,1248 .any_type, .type_expr => |node| return node,
1005 .var_type, .type_expr => |node| return node,
1006 }1249 }
1007 }1250 }
1008 i -= 1;1251 i -= 1;
...@@ -1015,34 +1258,79 @@ pub const Node = struct {...@@ -1015,34 +1258,79 @@ pub const Node = struct {
1015 if (self.noalias_token) |noalias_token| return noalias_token;1258 if (self.noalias_token) |noalias_token| return noalias_token;
1016 if (self.name_token) |name_token| return name_token;1259 if (self.name_token) |name_token| return name_token;
1017 switch (self.param_type) {1260 switch (self.param_type) {
1018 .var_args => |tok| return tok,1261 .any_type, .type_expr => |node| return node.firstToken(),
1019 .var_type, .type_expr => |node| return node.firstToken(),
1020 }1262 }
1021 }1263 }
10221264
1023 pub fn lastToken(self: *const ParamDecl) TokenIndex {1265 pub fn lastToken(self: *const ParamDecl) TokenIndex {
1024 switch (self.param_type) {1266 switch (self.param_type) {
1025 .var_args => |tok| return tok,1267 .any_type, .type_expr => |node| return node.lastToken(),
1026 .var_type, .type_expr => |node| return node.lastToken(),
1027 }1268 }
1028 }1269 }
1029 };1270 };
10301271
1272 /// For debugging purposes.
1273 pub fn dump(self: *const FnProto) void {
1274 const trailers_start = @alignCast(
1275 @alignOf(ParamDecl),
1276 @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
1277 );
1278 std.debug.print("{*} flags: {b} name_token: {} {*} params_len: {}\n", .{
1279 self,
1280 self.trailer_flags.bits,
1281 self.getTrailer("name_token"),
1282 self.trailer_flags.ptrConst(trailers_start, "name_token"),
1283 self.params_len,
1284 });
1285 }
1286
1287 pub fn getTrailer(self: *const FnProto, comptime name: []const u8) ?TrailerFlags.Field(name) {
1288 const trailers_start = @alignCast(
1289 @alignOf(ParamDecl),
1290 @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
1291 );
1292 return self.trailer_flags.get(trailers_start, name);
1293 }
1294
1295 pub fn setTrailer(self: *FnProto, comptime name: []const u8, value: TrailerFlags.Field(name)) void {
1296 const trailers_start = @alignCast(
1297 @alignOf(ParamDecl),
1298 @ptrCast([*]u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len,
1299 );
1300 self.trailer_flags.set(trailers_start, name, value);
1301 }
1302
1031 /// After this the caller must initialize the params list.1303 /// After this the caller must initialize the params list.
1032 pub fn alloc(allocator: *mem.Allocator, params_len: NodeIndex) !*FnProto {1304 pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: anytype) !*FnProto {
1033 const bytes = try allocator.alignedAlloc(u8, @alignOf(FnProto), sizeInBytes(params_len));1305 const trailer_flags = TrailerFlags.init(trailers);
1034 return @ptrCast(*FnProto, bytes.ptr);1306 const bytes = try allocator.alignedAlloc(u8, @alignOf(FnProto), sizeInBytes(
1307 required.params_len,
1308 trailer_flags,
1309 ));
1310 const fn_proto = @ptrCast(*FnProto, bytes.ptr);
1311 fn_proto.* = .{
1312 .trailer_flags = trailer_flags,
1313 .fn_token = required.fn_token,
1314 .params_len = required.params_len,
1315 .return_type = required.return_type,
1316 };
1317 const trailers_start = @alignCast(
1318 @alignOf(ParamDecl),
1319 bytes.ptr + @sizeOf(FnProto) + @sizeOf(ParamDecl) * required.params_len,
1320 );
1321 trailer_flags.setMany(trailers_start, trailers);
1322 return fn_proto;
1035 }1323 }
10361324
1037 pub fn free(self: *FnProto, allocator: *mem.Allocator) void {1325 pub fn destroy(self: *FnProto, allocator: *mem.Allocator) void {
1038 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len)];1326 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len, self.trailer_flags)];
1039 allocator.free(bytes);1327 allocator.free(bytes);
1040 }1328 }
10411329
1042 pub fn iterate(self: *const FnProto, index: usize) ?*Node {1330 pub fn iterate(self: *const FnProto, index: usize) ?*Node {
1043 var i = index;1331 var i = index;
10441332
1045 if (self.lib_name) |lib_name| {1333 if (self.getTrailer("lib_name")) |lib_name| {
1046 if (i < 1) return lib_name;1334 if (i < 1) return lib_name;
1047 i -= 1;1335 i -= 1;
1048 }1336 }
...@@ -1050,24 +1338,22 @@ pub const Node = struct {...@@ -1050,24 +1338,22 @@ pub const Node = struct {
1050 const params_len: usize = if (self.params_len == 0)1338 const params_len: usize = if (self.params_len == 0)
1051 01339 0
1052 else switch (self.paramsConst()[self.params_len - 1].param_type) {1340 else switch (self.paramsConst()[self.params_len - 1].param_type) {
1053 .var_type, .type_expr => self.params_len,1341 .any_type, .type_expr => self.params_len,
1054 .var_args => self.params_len - 1,
1055 };1342 };
1056 if (i < params_len) {1343 if (i < params_len) {
1057 switch (self.paramsConst()[i].param_type) {1344 switch (self.paramsConst()[i].param_type) {
1058 .var_type => |n| return n,1345 .any_type => |n| return n,
1059 .var_args => unreachable,
1060 .type_expr => |n| return n,1346 .type_expr => |n| return n,
1061 }1347 }
1062 }1348 }
1063 i -= params_len;1349 i -= params_len;
10641350
1065 if (self.align_expr) |align_expr| {1351 if (self.getTrailer("align_expr")) |align_expr| {
1066 if (i < 1) return align_expr;1352 if (i < 1) return align_expr;
1067 i -= 1;1353 i -= 1;
1068 }1354 }
10691355
1070 if (self.section_expr) |section_expr| {1356 if (self.getTrailer("section_expr")) |section_expr| {
1071 if (i < 1) return section_expr;1357 if (i < 1) return section_expr;
1072 i -= 1;1358 i -= 1;
1073 }1359 }
...@@ -1080,7 +1366,7 @@ pub const Node = struct {...@@ -1080,7 +1366,7 @@ pub const Node = struct {
1080 .Invalid => {},1366 .Invalid => {},
1081 }1367 }
10821368
1083 if (self.body_node) |body_node| {1369 if (self.getTrailer("body_node")) |body_node| {
1084 if (i < 1) return body_node;1370 if (i < 1) return body_node;
1085 i -= 1;1371 i -= 1;
1086 }1372 }
...@@ -1089,14 +1375,14 @@ pub const Node = struct {...@@ -1089,14 +1375,14 @@ pub const Node = struct {
1089 }1375 }
10901376
1091 pub fn firstToken(self: *const FnProto) TokenIndex {1377 pub fn firstToken(self: *const FnProto) TokenIndex {
1092 if (self.visib_token) |visib_token| return visib_token;1378 if (self.getTrailer("visib_token")) |visib_token| return visib_token;
1093 if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;1379 if (self.getTrailer("extern_export_inline_token")) |extern_export_inline_token| return extern_export_inline_token;
1094 assert(self.lib_name == null);1380 assert(self.getTrailer("lib_name") == null);
1095 return self.fn_token;1381 return self.fn_token;
1096 }1382 }
10971383
1098 pub fn lastToken(self: *const FnProto) TokenIndex {1384 pub fn lastToken(self: *const FnProto) TokenIndex {
1099 if (self.body_node) |body_node| return body_node.lastToken();1385 if (self.getTrailer("body_node")) |body_node| return body_node.lastToken();
1100 switch (self.return_type) {1386 switch (self.return_type) {
1101 .Explicit, .InferErrorSet => |node| return node.lastToken(),1387 .Explicit, .InferErrorSet => |node| return node.lastToken(),
1102 .Invalid => |tok| return tok,1388 .Invalid => |tok| return tok,
...@@ -1104,22 +1390,22 @@ pub const Node = struct {...@@ -1104,22 +1390,22 @@ pub const Node = struct {
1104 }1390 }
11051391
1106 pub fn params(self: *FnProto) []ParamDecl {1392 pub fn params(self: *FnProto) []ParamDecl {
1107 const decls_start = @ptrCast([*]u8, self) + @sizeOf(FnProto);1393 const params_start = @ptrCast([*]u8, self) + @sizeOf(FnProto);
1108 return @ptrCast([*]ParamDecl, decls_start)[0..self.params_len];1394 return @ptrCast([*]ParamDecl, params_start)[0..self.params_len];
1109 }1395 }
11101396
1111 pub fn paramsConst(self: *const FnProto) []const ParamDecl {1397 pub fn paramsConst(self: *const FnProto) []const ParamDecl {
1112 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(FnProto);1398 const params_start = @ptrCast([*]const u8, self) + @sizeOf(FnProto);
1113 return @ptrCast([*]const ParamDecl, decls_start)[0..self.params_len];1399 return @ptrCast([*]const ParamDecl, params_start)[0..self.params_len];
1114 }1400 }
11151401
1116 fn sizeInBytes(params_len: NodeIndex) usize {1402 fn sizeInBytes(params_len: NodeIndex, trailer_flags: TrailerFlags) usize {
1117 return @sizeOf(FnProto) + @sizeOf(ParamDecl) * @as(usize, params_len);1403 return @sizeOf(FnProto) + @sizeOf(ParamDecl) * @as(usize, params_len) + trailer_flags.sizeInBytes();
1118 }1404 }
1119 };1405 };
11201406
1121 pub const AnyFrameType = struct {1407 pub const AnyFrameType = struct {
1122 base: Node = Node{ .id = .AnyFrameType },1408 base: Node = Node{ .tag = .AnyFrameType },
1123 anyframe_token: TokenIndex,1409 anyframe_token: TokenIndex,
1124 result: ?Result,1410 result: ?Result,
11251411
...@@ -1151,7 +1437,7 @@ pub const Node = struct {...@@ -1151,7 +1437,7 @@ pub const Node = struct {
11511437
1152 /// The statements of the block follow Block directly in memory.1438 /// The statements of the block follow Block directly in memory.
1153 pub const Block = struct {1439 pub const Block = struct {
1154 base: Node = Node{ .id = .Block },1440 base: Node = Node{ .tag = .Block },
1155 statements_len: NodeIndex,1441 statements_len: NodeIndex,
1156 lbrace: TokenIndex,1442 lbrace: TokenIndex,
1157 rbrace: TokenIndex,1443 rbrace: TokenIndex,
...@@ -1205,7 +1491,7 @@ pub const Node = struct {...@@ -1205,7 +1491,7 @@ pub const Node = struct {
1205 };1491 };
12061492
1207 pub const Defer = struct {1493 pub const Defer = struct {
1208 base: Node = Node{ .id = .Defer },1494 base: Node = Node{ .tag = .Defer },
1209 defer_token: TokenIndex,1495 defer_token: TokenIndex,
1210 payload: ?*Node,1496 payload: ?*Node,
1211 expr: *Node,1497 expr: *Node,
...@@ -1229,7 +1515,7 @@ pub const Node = struct {...@@ -1229,7 +1515,7 @@ pub const Node = struct {
1229 };1515 };
12301516
1231 pub const Comptime = struct {1517 pub const Comptime = struct {
1232 base: Node = Node{ .id = .Comptime },1518 base: Node = Node{ .tag = .Comptime },
1233 doc_comments: ?*DocComment,1519 doc_comments: ?*DocComment,
1234 comptime_token: TokenIndex,1520 comptime_token: TokenIndex,
1235 expr: *Node,1521 expr: *Node,
...@@ -1253,7 +1539,7 @@ pub const Node = struct {...@@ -1253,7 +1539,7 @@ pub const Node = struct {
1253 };1539 };
12541540
1255 pub const Nosuspend = struct {1541 pub const Nosuspend = struct {
1256 base: Node = Node{ .id = .Nosuspend },1542 base: Node = Node{ .tag = .Nosuspend },
1257 nosuspend_token: TokenIndex,1543 nosuspend_token: TokenIndex,
1258 expr: *Node,1544 expr: *Node,
12591545
...@@ -1276,7 +1562,7 @@ pub const Node = struct {...@@ -1276,7 +1562,7 @@ pub const Node = struct {
1276 };1562 };
12771563
1278 pub const Payload = struct {1564 pub const Payload = struct {
1279 base: Node = Node{ .id = .Payload },1565 base: Node = Node{ .tag = .Payload },
1280 lpipe: TokenIndex,1566 lpipe: TokenIndex,
1281 error_symbol: *Node,1567 error_symbol: *Node,
1282 rpipe: TokenIndex,1568 rpipe: TokenIndex,
...@@ -1300,7 +1586,7 @@ pub const Node = struct {...@@ -1300,7 +1586,7 @@ pub const Node = struct {
1300 };1586 };
13011587
1302 pub const PointerPayload = struct {1588 pub const PointerPayload = struct {
1303 base: Node = Node{ .id = .PointerPayload },1589 base: Node = Node{ .tag = .PointerPayload },
1304 lpipe: TokenIndex,1590 lpipe: TokenIndex,
1305 ptr_token: ?TokenIndex,1591 ptr_token: ?TokenIndex,
1306 value_symbol: *Node,1592 value_symbol: *Node,
...@@ -1325,7 +1611,7 @@ pub const Node = struct {...@@ -1325,7 +1611,7 @@ pub const Node = struct {
1325 };1611 };
13261612
1327 pub const PointerIndexPayload = struct {1613 pub const PointerIndexPayload = struct {
1328 base: Node = Node{ .id = .PointerIndexPayload },1614 base: Node = Node{ .tag = .PointerIndexPayload },
1329 lpipe: TokenIndex,1615 lpipe: TokenIndex,
1330 ptr_token: ?TokenIndex,1616 ptr_token: ?TokenIndex,
1331 value_symbol: *Node,1617 value_symbol: *Node,
...@@ -1356,7 +1642,7 @@ pub const Node = struct {...@@ -1356,7 +1642,7 @@ pub const Node = struct {
1356 };1642 };
13571643
1358 pub const Else = struct {1644 pub const Else = struct {
1359 base: Node = Node{ .id = .Else },1645 base: Node = Node{ .tag = .Else },
1360 else_token: TokenIndex,1646 else_token: TokenIndex,
1361 payload: ?*Node,1647 payload: ?*Node,
1362 body: *Node,1648 body: *Node,
...@@ -1387,7 +1673,7 @@ pub const Node = struct {...@@ -1387,7 +1673,7 @@ pub const Node = struct {
1387 /// The cases node pointers are found in memory after Switch.1673 /// The cases node pointers are found in memory after Switch.
1388 /// They must be SwitchCase or SwitchElse nodes.1674 /// They must be SwitchCase or SwitchElse nodes.
1389 pub const Switch = struct {1675 pub const Switch = struct {
1390 base: Node = Node{ .id = .Switch },1676 base: Node = Node{ .tag = .Switch },
1391 switch_token: TokenIndex,1677 switch_token: TokenIndex,
1392 rbrace: TokenIndex,1678 rbrace: TokenIndex,
1393 cases_len: NodeIndex,1679 cases_len: NodeIndex,
...@@ -1441,7 +1727,7 @@ pub const Node = struct {...@@ -1441,7 +1727,7 @@ pub const Node = struct {
14411727
1442 /// Items sub-nodes appear in memory directly following SwitchCase.1728 /// Items sub-nodes appear in memory directly following SwitchCase.
1443 pub const SwitchCase = struct {1729 pub const SwitchCase = struct {
1444 base: Node = Node{ .id = .SwitchCase },1730 base: Node = Node{ .tag = .SwitchCase },
1445 arrow_token: TokenIndex,1731 arrow_token: TokenIndex,
1446 payload: ?*Node,1732 payload: ?*Node,
1447 expr: *Node,1733 expr: *Node,
...@@ -1499,7 +1785,7 @@ pub const Node = struct {...@@ -1499,7 +1785,7 @@ pub const Node = struct {
1499 };1785 };
15001786
1501 pub const SwitchElse = struct {1787 pub const SwitchElse = struct {
1502 base: Node = Node{ .id = .SwitchElse },1788 base: Node = Node{ .tag = .SwitchElse },
1503 token: TokenIndex,1789 token: TokenIndex,
15041790
1505 pub fn iterate(self: *const SwitchElse, index: usize) ?*Node {1791 pub fn iterate(self: *const SwitchElse, index: usize) ?*Node {
...@@ -1516,7 +1802,7 @@ pub const Node = struct {...@@ -1516,7 +1802,7 @@ pub const Node = struct {
1516 };1802 };
15171803
1518 pub const While = struct {1804 pub const While = struct {
1519 base: Node = Node{ .id = .While },1805 base: Node = Node{ .tag = .While },
1520 label: ?TokenIndex,1806 label: ?TokenIndex,
1521 inline_token: ?TokenIndex,1807 inline_token: ?TokenIndex,
1522 while_token: TokenIndex,1808 while_token: TokenIndex,
...@@ -1575,7 +1861,7 @@ pub const Node = struct {...@@ -1575,7 +1861,7 @@ pub const Node = struct {
1575 };1861 };
15761862
1577 pub const For = struct {1863 pub const For = struct {
1578 base: Node = Node{ .id = .For },1864 base: Node = Node{ .tag = .For },
1579 label: ?TokenIndex,1865 label: ?TokenIndex,
1580 inline_token: ?TokenIndex,1866 inline_token: ?TokenIndex,
1581 for_token: TokenIndex,1867 for_token: TokenIndex,
...@@ -1626,7 +1912,7 @@ pub const Node = struct {...@@ -1626,7 +1912,7 @@ pub const Node = struct {
1626 };1912 };
16271913
1628 pub const If = struct {1914 pub const If = struct {
1629 base: Node = Node{ .id = .If },1915 base: Node = Node{ .tag = .If },
1630 if_token: TokenIndex,1916 if_token: TokenIndex,
1631 condition: *Node,1917 condition: *Node,
1632 payload: ?*Node,1918 payload: ?*Node,
...@@ -1668,116 +1954,22 @@ pub const Node = struct {...@@ -1668,116 +1954,22 @@ pub const Node = struct {
1668 }1954 }
1669 };1955 };
16701956
1671 pub const InfixOp = struct {1957 pub const Catch = struct {
1672 base: Node = Node{ .id = .InfixOp },1958 base: Node = Node{ .tag = .Catch },
1673 op_token: TokenIndex,1959 op_token: TokenIndex,
1674 lhs: *Node,1960 lhs: *Node,
1675 op: Op,
1676 rhs: *Node,1961 rhs: *Node,
1962 payload: ?*Node,
16771963
1678 pub const Op = union(enum) {1964 pub fn iterate(self: *const Catch, index: usize) ?*Node {
1679 Add,
1680 AddWrap,
1681 ArrayCat,
1682 ArrayMult,
1683 Assign,
1684 AssignBitAnd,
1685 AssignBitOr,
1686 AssignBitShiftLeft,
1687 AssignBitShiftRight,
1688 AssignBitXor,
1689 AssignDiv,
1690 AssignSub,
1691 AssignSubWrap,
1692 AssignMod,
1693 AssignAdd,
1694 AssignAddWrap,
1695 AssignMul,
1696 AssignMulWrap,
1697 BangEqual,
1698 BitAnd,
1699 BitOr,
1700 BitShiftLeft,
1701 BitShiftRight,
1702 BitXor,
1703 BoolAnd,
1704 BoolOr,
1705 Catch: ?*Node,
1706 Div,
1707 EqualEqual,
1708 ErrorUnion,
1709 GreaterOrEqual,
1710 GreaterThan,
1711 LessOrEqual,
1712 LessThan,
1713 MergeErrorSets,
1714 Mod,
1715 Mul,
1716 MulWrap,
1717 Period,
1718 Range,
1719 Sub,
1720 SubWrap,
1721 UnwrapOptional,
1722 };
1723
1724 pub fn iterate(self: *const InfixOp, index: usize) ?*Node {
1725 var i = index;1965 var i = index;
17261966
1727 if (i < 1) return self.lhs;1967 if (i < 1) return self.lhs;
1728 i -= 1;1968 i -= 1;
17291969
1730 switch (self.op) {1970 if (self.payload) |payload| {
1731 .Catch => |maybe_payload| {1971 if (i < 1) return payload;
1732 if (maybe_payload) |payload| {1972 i -= 1;
1733 if (i < 1) return payload;
1734 i -= 1;
1735 }
1736 },
1737
1738 .Add,
1739 .AddWrap,
1740 .ArrayCat,
1741 .ArrayMult,
1742 .Assign,
1743 .AssignBitAnd,
1744 .AssignBitOr,
1745 .AssignBitShiftLeft,
1746 .AssignBitShiftRight,
1747 .AssignBitXor,
1748 .AssignDiv,
1749 .AssignSub,
1750 .AssignSubWrap,
1751 .AssignMod,
1752 .AssignAdd,
1753 .AssignAddWrap,
1754 .AssignMul,
1755 .AssignMulWrap,
1756 .BangEqual,
1757 .BitAnd,
1758 .BitOr,
1759 .BitShiftLeft,
1760 .BitShiftRight,
1761 .BitXor,
1762 .BoolAnd,
1763 .BoolOr,
1764 .Div,
1765 .EqualEqual,
1766 .ErrorUnion,
1767 .GreaterOrEqual,
1768 .GreaterThan,
1769 .LessOrEqual,
1770 .LessThan,
1771 .MergeErrorSets,
1772 .Mod,
1773 .Mul,
1774 .MulWrap,
1775 .Period,
1776 .Range,
1777 .Sub,
1778 .SubWrap,
1779 .UnwrapOptional,
1780 => {},
1781 }1973 }
17821974
1783 if (i < 1) return self.rhs;1975 if (i < 1) return self.rhs;
...@@ -1786,94 +1978,140 @@ pub const Node = struct {...@@ -1786,94 +1978,140 @@ pub const Node = struct {
1786 return null;1978 return null;
1787 }1979 }
17881980
1789 pub fn firstToken(self: *const InfixOp) TokenIndex {1981 pub fn firstToken(self: *const Catch) TokenIndex {
1790 return self.lhs.firstToken();1982 return self.lhs.firstToken();
1791 }1983 }
17921984
1793 pub fn lastToken(self: *const InfixOp) TokenIndex {1985 pub fn lastToken(self: *const Catch) TokenIndex {
1794 return self.rhs.lastToken();1986 return self.rhs.lastToken();
1795 }1987 }
1796 };1988 };
17971989
1798 pub const PrefixOp = struct {1990 pub const SimpleInfixOp = struct {
1799 base: Node = Node{ .id = .PrefixOp },1991 base: Node,
1800 op_token: TokenIndex,1992 op_token: TokenIndex,
1801 op: Op,1993 lhs: *Node,
1802 rhs: *Node,1994 rhs: *Node,
18031995
1804 pub const Op = union(enum) {1996 pub fn iterate(self: *const SimpleInfixOp, index: usize) ?*Node {
1805 AddressOf,1997 var i = index;
1806 ArrayType: ArrayInfo,
1807 Await,
1808 BitNot,
1809 BoolNot,
1810 OptionalType,
1811 Negation,
1812 NegationWrap,
1813 Resume,
1814 PtrType: PtrInfo,
1815 SliceType: PtrInfo,
1816 Try,
1817 };
18181998
1819 pub const ArrayInfo = struct {1999 if (i < 1) return self.lhs;
1820 len_expr: *Node,2000 i -= 1;
1821 sentinel: ?*Node,
1822 };
18232001
1824 pub const PtrInfo = struct {2002 if (i < 1) return self.rhs;
1825 allowzero_token: ?TokenIndex = null,2003 i -= 1;
1826 align_info: ?Align = null,2004
1827 const_token: ?TokenIndex = null,2005 return null;
1828 volatile_token: ?TokenIndex = null,2006 }
1829 sentinel: ?*Node = null,2007
18302008 pub fn firstToken(self: *const SimpleInfixOp) TokenIndex {
1831 pub const Align = struct {2009 return self.lhs.firstToken();
1832 node: *Node,2010 }
1833 bit_range: ?BitRange,2011
18342012 pub fn lastToken(self: *const SimpleInfixOp) TokenIndex {
1835 pub const BitRange = struct {2013 return self.rhs.lastToken();
1836 start: *Node,2014 }
1837 end: *Node,2015 };
1838 };2016
1839 };2017 pub const SimplePrefixOp = struct {
1840 };2018 base: Node,
2019 op_token: TokenIndex,
2020 rhs: *Node,
2021
2022 const Self = @This();
2023
2024 pub fn iterate(self: *const Self, index: usize) ?*Node {
2025 if (index == 0) return self.rhs;
2026 return null;
2027 }
18412028
1842 pub fn iterate(self: *const PrefixOp, index: usize) ?*Node {2029 pub fn firstToken(self: *const Self) TokenIndex {
2030 return self.op_token;
2031 }
2032
2033 pub fn lastToken(self: *const Self) TokenIndex {
2034 return self.rhs.lastToken();
2035 }
2036 };
2037
2038 pub const ArrayType = struct {
2039 base: Node = Node{ .tag = .ArrayType },
2040 op_token: TokenIndex,
2041 rhs: *Node,
2042 len_expr: *Node,
2043
2044 pub fn iterate(self: *const ArrayType, index: usize) ?*Node {
1843 var i = index;2045 var i = index;
18442046
1845 switch (self.op) {2047 if (i < 1) return self.len_expr;
1846 .PtrType, .SliceType => |addr_of_info| {2048 i -= 1;
1847 if (addr_of_info.sentinel) |sentinel| {
1848 if (i < 1) return sentinel;
1849 i -= 1;
1850 }
18512049
1852 if (addr_of_info.align_info) |align_info| {2050 if (i < 1) return self.rhs;
1853 if (i < 1) return align_info.node;2051 i -= 1;
1854 i -= 1;
1855 }
1856 },
18572052
1858 .ArrayType => |array_info| {2053 return null;
1859 if (i < 1) return array_info.len_expr;2054 }
1860 i -= 1;
1861 if (array_info.sentinel) |sentinel| {
1862 if (i < 1) return sentinel;
1863 i -= 1;
1864 }
1865 },
18662055
1867 .AddressOf,2056 pub fn firstToken(self: *const ArrayType) TokenIndex {
1868 .Await,2057 return self.op_token;
1869 .BitNot,2058 }
1870 .BoolNot,2059
1871 .OptionalType,2060 pub fn lastToken(self: *const ArrayType) TokenIndex {
1872 .Negation,2061 return self.rhs.lastToken();
1873 .NegationWrap,2062 }
1874 .Try,2063 };
1875 .Resume,2064
1876 => {},2065 pub const ArrayTypeSentinel = struct {
2066 base: Node = Node{ .tag = .ArrayTypeSentinel },
2067 op_token: TokenIndex,
2068 rhs: *Node,
2069 len_expr: *Node,
2070 sentinel: *Node,
2071
2072 pub fn iterate(self: *const ArrayTypeSentinel, index: usize) ?*Node {
2073 var i = index;
2074
2075 if (i < 1) return self.len_expr;
2076 i -= 1;
2077
2078 if (i < 1) return self.sentinel;
2079 i -= 1;
2080
2081 if (i < 1) return self.rhs;
2082 i -= 1;
2083
2084 return null;
2085 }
2086
2087 pub fn firstToken(self: *const ArrayTypeSentinel) TokenIndex {
2088 return self.op_token;
2089 }
2090
2091 pub fn lastToken(self: *const ArrayTypeSentinel) TokenIndex {
2092 return self.rhs.lastToken();
2093 }
2094 };
2095
2096 pub const PtrType = struct {
2097 base: Node = Node{ .tag = .PtrType },
2098 op_token: TokenIndex,
2099 rhs: *Node,
2100 /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
2101 /// one of these possibly-null things. Then we have them directly follow the PtrType in memory.
2102 ptr_info: PtrInfo = .{},
2103
2104 pub fn iterate(self: *const PtrType, index: usize) ?*Node {
2105 var i = index;
2106
2107 if (self.ptr_info.sentinel) |sentinel| {
2108 if (i < 1) return sentinel;
2109 i -= 1;
2110 }
2111
2112 if (self.ptr_info.align_info) |align_info| {
2113 if (i < 1) return align_info.node;
2114 i -= 1;
1877 }2115 }
18782116
1879 if (i < 1) return self.rhs;2117 if (i < 1) return self.rhs;
...@@ -1882,17 +2120,53 @@ pub const Node = struct {...@@ -1882,17 +2120,53 @@ pub const Node = struct {
1882 return null;2120 return null;
1883 }2121 }
18842122
1885 pub fn firstToken(self: *const PrefixOp) TokenIndex {2123 pub fn firstToken(self: *const PtrType) TokenIndex {
1886 return self.op_token;2124 return self.op_token;
1887 }2125 }
18882126
1889 pub fn lastToken(self: *const PrefixOp) TokenIndex {2127 pub fn lastToken(self: *const PtrType) TokenIndex {
2128 return self.rhs.lastToken();
2129 }
2130 };
2131
2132 pub const SliceType = struct {
2133 base: Node = Node{ .tag = .SliceType },
2134 op_token: TokenIndex,
2135 rhs: *Node,
2136 /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
2137 /// one of these possibly-null things. Then we have them directly follow the SliceType in memory.
2138 ptr_info: PtrInfo = .{},
2139
2140 pub fn iterate(self: *const SliceType, index: usize) ?*Node {
2141 var i = index;
2142
2143 if (self.ptr_info.sentinel) |sentinel| {
2144 if (i < 1) return sentinel;
2145 i -= 1;
2146 }
2147
2148 if (self.ptr_info.align_info) |align_info| {
2149 if (i < 1) return align_info.node;
2150 i -= 1;
2151 }
2152
2153 if (i < 1) return self.rhs;
2154 i -= 1;
2155
2156 return null;
2157 }
2158
2159 pub fn firstToken(self: *const SliceType) TokenIndex {
2160 return self.op_token;
2161 }
2162
2163 pub fn lastToken(self: *const SliceType) TokenIndex {
1890 return self.rhs.lastToken();2164 return self.rhs.lastToken();
1891 }2165 }
1892 };2166 };
18932167
1894 pub const FieldInitializer = struct {2168 pub const FieldInitializer = struct {
1895 base: Node = Node{ .id = .FieldInitializer },2169 base: Node = Node{ .tag = .FieldInitializer },
1896 period_token: TokenIndex,2170 period_token: TokenIndex,
1897 name_token: TokenIndex,2171 name_token: TokenIndex,
1898 expr: *Node,2172 expr: *Node,
...@@ -1917,7 +2191,7 @@ pub const Node = struct {...@@ -1917,7 +2191,7 @@ pub const Node = struct {
19172191
1918 /// Elements occur directly in memory after ArrayInitializer.2192 /// Elements occur directly in memory after ArrayInitializer.
1919 pub const ArrayInitializer = struct {2193 pub const ArrayInitializer = struct {
1920 base: Node = Node{ .id = .ArrayInitializer },2194 base: Node = Node{ .tag = .ArrayInitializer },
1921 rtoken: TokenIndex,2195 rtoken: TokenIndex,
1922 list_len: NodeIndex,2196 list_len: NodeIndex,
1923 lhs: *Node,2197 lhs: *Node,
...@@ -1970,7 +2244,7 @@ pub const Node = struct {...@@ -1970,7 +2244,7 @@ pub const Node = struct {
19702244
1971 /// Elements occur directly in memory after ArrayInitializerDot.2245 /// Elements occur directly in memory after ArrayInitializerDot.
1972 pub const ArrayInitializerDot = struct {2246 pub const ArrayInitializerDot = struct {
1973 base: Node = Node{ .id = .ArrayInitializerDot },2247 base: Node = Node{ .tag = .ArrayInitializerDot },
1974 dot: TokenIndex,2248 dot: TokenIndex,
1975 rtoken: TokenIndex,2249 rtoken: TokenIndex,
1976 list_len: NodeIndex,2250 list_len: NodeIndex,
...@@ -2020,7 +2294,7 @@ pub const Node = struct {...@@ -2020,7 +2294,7 @@ pub const Node = struct {
20202294
2021 /// Elements occur directly in memory after StructInitializer.2295 /// Elements occur directly in memory after StructInitializer.
2022 pub const StructInitializer = struct {2296 pub const StructInitializer = struct {
2023 base: Node = Node{ .id = .StructInitializer },2297 base: Node = Node{ .tag = .StructInitializer },
2024 rtoken: TokenIndex,2298 rtoken: TokenIndex,
2025 list_len: NodeIndex,2299 list_len: NodeIndex,
2026 lhs: *Node,2300 lhs: *Node,
...@@ -2073,7 +2347,7 @@ pub const Node = struct {...@@ -2073,7 +2347,7 @@ pub const Node = struct {
20732347
2074 /// Elements occur directly in memory after StructInitializerDot.2348 /// Elements occur directly in memory after StructInitializerDot.
2075 pub const StructInitializerDot = struct {2349 pub const StructInitializerDot = struct {
2076 base: Node = Node{ .id = .StructInitializerDot },2350 base: Node = Node{ .tag = .StructInitializerDot },
2077 dot: TokenIndex,2351 dot: TokenIndex,
2078 rtoken: TokenIndex,2352 rtoken: TokenIndex,
2079 list_len: NodeIndex,2353 list_len: NodeIndex,
...@@ -2123,7 +2397,7 @@ pub const Node = struct {...@@ -2123,7 +2397,7 @@ pub const Node = struct {
21232397
2124 /// Parameter nodes directly follow Call in memory.2398 /// Parameter nodes directly follow Call in memory.
2125 pub const Call = struct {2399 pub const Call = struct {
2126 base: Node = Node{ .id = .Call },2400 base: Node = Node{ .tag = .Call },
2127 lhs: *Node,2401 lhs: *Node,
2128 rtoken: TokenIndex,2402 rtoken: TokenIndex,
2129 params_len: NodeIndex,2403 params_len: NodeIndex,
...@@ -2177,7 +2451,7 @@ pub const Node = struct {...@@ -2177,7 +2451,7 @@ pub const Node = struct {
2177 };2451 };
21782452
2179 pub const SuffixOp = struct {2453 pub const SuffixOp = struct {
2180 base: Node = Node{ .id = .SuffixOp },2454 base: Node = Node{ .tag = .SuffixOp },
2181 op: Op,2455 op: Op,
2182 lhs: *Node,2456 lhs: *Node,
2183 rtoken: TokenIndex,2457 rtoken: TokenIndex,
...@@ -2237,7 +2511,7 @@ pub const Node = struct {...@@ -2237,7 +2511,7 @@ pub const Node = struct {
2237 };2511 };
22382512
2239 pub const GroupedExpression = struct {2513 pub const GroupedExpression = struct {
2240 base: Node = Node{ .id = .GroupedExpression },2514 base: Node = Node{ .tag = .GroupedExpression },
2241 lparen: TokenIndex,2515 lparen: TokenIndex,
2242 expr: *Node,2516 expr: *Node,
2243 rparen: TokenIndex,2517 rparen: TokenIndex,
...@@ -2260,8 +2534,10 @@ pub const Node = struct {...@@ -2260,8 +2534,10 @@ pub const Node = struct {
2260 }2534 }
2261 };2535 };
22622536
2537 /// TODO break this into separate Break, Continue, Return AST Nodes to save memory.
2538 /// Could be further broken into LabeledBreak, LabeledContinue, and ReturnVoid to save even more.
2263 pub const ControlFlowExpression = struct {2539 pub const ControlFlowExpression = struct {
2264 base: Node = Node{ .id = .ControlFlowExpression },2540 base: Node = Node{ .tag = .ControlFlowExpression },
2265 ltoken: TokenIndex,2541 ltoken: TokenIndex,
2266 kind: Kind,2542 kind: Kind,
2267 rhs: ?*Node,2543 rhs: ?*Node,
...@@ -2316,7 +2592,7 @@ pub const Node = struct {...@@ -2316,7 +2592,7 @@ pub const Node = struct {
2316 };2592 };
23172593
2318 pub const Suspend = struct {2594 pub const Suspend = struct {
2319 base: Node = Node{ .id = .Suspend },2595 base: Node = Node{ .tag = .Suspend },
2320 suspend_token: TokenIndex,2596 suspend_token: TokenIndex,
2321 body: ?*Node,2597 body: ?*Node,
23222598
...@@ -2345,7 +2621,7 @@ pub const Node = struct {...@@ -2345,7 +2621,7 @@ pub const Node = struct {
2345 };2621 };
23462622
2347 pub const IntegerLiteral = struct {2623 pub const IntegerLiteral = struct {
2348 base: Node = Node{ .id = .IntegerLiteral },2624 base: Node = Node{ .tag = .IntegerLiteral },
2349 token: TokenIndex,2625 token: TokenIndex,
23502626
2351 pub fn iterate(self: *const IntegerLiteral, index: usize) ?*Node {2627 pub fn iterate(self: *const IntegerLiteral, index: usize) ?*Node {
...@@ -2362,7 +2638,7 @@ pub const Node = struct {...@@ -2362,7 +2638,7 @@ pub const Node = struct {
2362 };2638 };
23632639
2364 pub const EnumLiteral = struct {2640 pub const EnumLiteral = struct {
2365 base: Node = Node{ .id = .EnumLiteral },2641 base: Node = Node{ .tag = .EnumLiteral },
2366 dot: TokenIndex,2642 dot: TokenIndex,
2367 name: TokenIndex,2643 name: TokenIndex,
23682644
...@@ -2380,7 +2656,7 @@ pub const Node = struct {...@@ -2380,7 +2656,7 @@ pub const Node = struct {
2380 };2656 };
23812657
2382 pub const FloatLiteral = struct {2658 pub const FloatLiteral = struct {
2383 base: Node = Node{ .id = .FloatLiteral },2659 base: Node = Node{ .tag = .FloatLiteral },
2384 token: TokenIndex,2660 token: TokenIndex,
23852661
2386 pub fn iterate(self: *const FloatLiteral, index: usize) ?*Node {2662 pub fn iterate(self: *const FloatLiteral, index: usize) ?*Node {
...@@ -2398,7 +2674,7 @@ pub const Node = struct {...@@ -2398,7 +2674,7 @@ pub const Node = struct {
23982674
2399 /// Parameters are in memory following BuiltinCall.2675 /// Parameters are in memory following BuiltinCall.
2400 pub const BuiltinCall = struct {2676 pub const BuiltinCall = struct {
2401 base: Node = Node{ .id = .BuiltinCall },2677 base: Node = Node{ .tag = .BuiltinCall },
2402 params_len: NodeIndex,2678 params_len: NodeIndex,
2403 builtin_token: TokenIndex,2679 builtin_token: TokenIndex,
2404 rparen_token: TokenIndex,2680 rparen_token: TokenIndex,
...@@ -2447,7 +2723,7 @@ pub const Node = struct {...@@ -2447,7 +2723,7 @@ pub const Node = struct {
2447 };2723 };
24482724
2449 pub const StringLiteral = struct {2725 pub const StringLiteral = struct {
2450 base: Node = Node{ .id = .StringLiteral },2726 base: Node = Node{ .tag = .StringLiteral },
2451 token: TokenIndex,2727 token: TokenIndex,
24522728
2453 pub fn iterate(self: *const StringLiteral, index: usize) ?*Node {2729 pub fn iterate(self: *const StringLiteral, index: usize) ?*Node {
...@@ -2465,7 +2741,7 @@ pub const Node = struct {...@@ -2465,7 +2741,7 @@ pub const Node = struct {
24652741
2466 /// The string literal tokens appear directly in memory after MultilineStringLiteral.2742 /// The string literal tokens appear directly in memory after MultilineStringLiteral.
2467 pub const MultilineStringLiteral = struct {2743 pub const MultilineStringLiteral = struct {
2468 base: Node = Node{ .id = .MultilineStringLiteral },2744 base: Node = Node{ .tag = .MultilineStringLiteral },
2469 lines_len: TokenIndex,2745 lines_len: TokenIndex,
24702746
2471 /// After this the caller must initialize the lines list.2747 /// After this the caller must initialize the lines list.
...@@ -2507,7 +2783,7 @@ pub const Node = struct {...@@ -2507,7 +2783,7 @@ pub const Node = struct {
2507 };2783 };
25082784
2509 pub const CharLiteral = struct {2785 pub const CharLiteral = struct {
2510 base: Node = Node{ .id = .CharLiteral },2786 base: Node = Node{ .tag = .CharLiteral },
2511 token: TokenIndex,2787 token: TokenIndex,
25122788
2513 pub fn iterate(self: *const CharLiteral, index: usize) ?*Node {2789 pub fn iterate(self: *const CharLiteral, index: usize) ?*Node {
...@@ -2524,7 +2800,7 @@ pub const Node = struct {...@@ -2524,7 +2800,7 @@ pub const Node = struct {
2524 };2800 };
25252801
2526 pub const BoolLiteral = struct {2802 pub const BoolLiteral = struct {
2527 base: Node = Node{ .id = .BoolLiteral },2803 base: Node = Node{ .tag = .BoolLiteral },
2528 token: TokenIndex,2804 token: TokenIndex,
25292805
2530 pub fn iterate(self: *const BoolLiteral, index: usize) ?*Node {2806 pub fn iterate(self: *const BoolLiteral, index: usize) ?*Node {
...@@ -2541,7 +2817,7 @@ pub const Node = struct {...@@ -2541,7 +2817,7 @@ pub const Node = struct {
2541 };2817 };
25422818
2543 pub const NullLiteral = struct {2819 pub const NullLiteral = struct {
2544 base: Node = Node{ .id = .NullLiteral },2820 base: Node = Node{ .tag = .NullLiteral },
2545 token: TokenIndex,2821 token: TokenIndex,
25462822
2547 pub fn iterate(self: *const NullLiteral, index: usize) ?*Node {2823 pub fn iterate(self: *const NullLiteral, index: usize) ?*Node {
...@@ -2558,7 +2834,7 @@ pub const Node = struct {...@@ -2558,7 +2834,7 @@ pub const Node = struct {
2558 };2834 };
25592835
2560 pub const UndefinedLiteral = struct {2836 pub const UndefinedLiteral = struct {
2561 base: Node = Node{ .id = .UndefinedLiteral },2837 base: Node = Node{ .tag = .UndefinedLiteral },
2562 token: TokenIndex,2838 token: TokenIndex,
25632839
2564 pub fn iterate(self: *const UndefinedLiteral, index: usize) ?*Node {2840 pub fn iterate(self: *const UndefinedLiteral, index: usize) ?*Node {
...@@ -2575,7 +2851,7 @@ pub const Node = struct {...@@ -2575,7 +2851,7 @@ pub const Node = struct {
2575 };2851 };
25762852
2577 pub const Asm = struct {2853 pub const Asm = struct {
2578 base: Node = Node{ .id = .Asm },2854 base: Node = Node{ .tag = .Asm },
2579 asm_token: TokenIndex,2855 asm_token: TokenIndex,
2580 rparen: TokenIndex,2856 rparen: TokenIndex,
2581 volatile_token: ?TokenIndex,2857 volatile_token: ?TokenIndex,
...@@ -2695,7 +2971,7 @@ pub const Node = struct {...@@ -2695,7 +2971,7 @@ pub const Node = struct {
2695 };2971 };
26962972
2697 pub const Unreachable = struct {2973 pub const Unreachable = struct {
2698 base: Node = Node{ .id = .Unreachable },2974 base: Node = Node{ .tag = .Unreachable },
2699 token: TokenIndex,2975 token: TokenIndex,
27002976
2701 pub fn iterate(self: *const Unreachable, index: usize) ?*Node {2977 pub fn iterate(self: *const Unreachable, index: usize) ?*Node {
...@@ -2712,7 +2988,7 @@ pub const Node = struct {...@@ -2712,7 +2988,7 @@ pub const Node = struct {
2712 };2988 };
27132989
2714 pub const ErrorType = struct {2990 pub const ErrorType = struct {
2715 base: Node = Node{ .id = .ErrorType },2991 base: Node = Node{ .tag = .ErrorType },
2716 token: TokenIndex,2992 token: TokenIndex,
27172993
2718 pub fn iterate(self: *const ErrorType, index: usize) ?*Node {2994 pub fn iterate(self: *const ErrorType, index: usize) ?*Node {
...@@ -2728,25 +3004,28 @@ pub const Node = struct {...@@ -2728,25 +3004,28 @@ pub const Node = struct {
2728 }3004 }
2729 };3005 };
27303006
2731 pub const VarType = struct {3007 pub const AnyType = struct {
2732 base: Node = Node{ .id = .VarType },3008 base: Node = Node{ .tag = .AnyType },
2733 token: TokenIndex,3009 token: TokenIndex,
27343010
2735 pub fn iterate(self: *const VarType, index: usize) ?*Node {3011 pub fn iterate(self: *const AnyType, index: usize) ?*Node {
2736 return null;3012 return null;
2737 }3013 }
27383014
2739 pub fn firstToken(self: *const VarType) TokenIndex {3015 pub fn firstToken(self: *const AnyType) TokenIndex {
2740 return self.token;3016 return self.token;
2741 }3017 }
27423018
2743 pub fn lastToken(self: *const VarType) TokenIndex {3019 pub fn lastToken(self: *const AnyType) TokenIndex {
2744 return self.token;3020 return self.token;
2745 }3021 }
2746 };3022 };
27473023
3024 /// TODO remove from the Node base struct
3025 /// TODO actually maybe remove entirely in favor of iterating backward from Node.firstToken()
3026 /// and forwards to find same-line doc comments.
2748 pub const DocComment = struct {3027 pub const DocComment = struct {
2749 base: Node = Node{ .id = .DocComment },3028 base: Node = Node{ .tag = .DocComment },
2750 /// Points to the first doc comment token. API users are expected to iterate over the3029 /// Points to the first doc comment token. API users are expected to iterate over the
2751 /// tokens array, looking for more doc comments, ignoring line comments, and stopping3030 /// tokens array, looking for more doc comments, ignoring line comments, and stopping
2752 /// at the first other token.3031 /// at the first other token.
...@@ -2768,7 +3047,7 @@ pub const Node = struct {...@@ -2768,7 +3047,7 @@ pub const Node = struct {
2768 };3047 };
27693048
2770 pub const TestDecl = struct {3049 pub const TestDecl = struct {
2771 base: Node = Node{ .id = .TestDecl },3050 base: Node = Node{ .tag = .TestDecl },
2772 doc_comments: ?*DocComment,3051 doc_comments: ?*DocComment,
2773 test_token: TokenIndex,3052 test_token: TokenIndex,
2774 name: *Node,3053 name: *Node,
...@@ -2793,9 +3072,27 @@ pub const Node = struct {...@@ -2793,9 +3072,27 @@ pub const Node = struct {
2793 };3072 };
2794};3073};
27953074
3075pub const PtrInfo = struct {
3076 allowzero_token: ?TokenIndex = null,
3077 align_info: ?Align = null,
3078 const_token: ?TokenIndex = null,
3079 volatile_token: ?TokenIndex = null,
3080 sentinel: ?*Node = null,
3081
3082 pub const Align = struct {
3083 node: *Node,
3084 bit_range: ?BitRange = null,
3085
3086 pub const BitRange = struct {
3087 start: *Node,
3088 end: *Node,
3089 };
3090 };
3091};
3092
2796test "iterate" {3093test "iterate" {
2797 var root = Node.Root{3094 var root = Node.Root{
2798 .base = Node{ .id = Node.Id.Root },3095 .base = Node{ .tag = Node.Tag.Root },
2799 .decls_len = 0,3096 .decls_len = 0,
2800 .eof_token = 0,3097 .eof_token = 0,
2801 };3098 };
lib/std/zig/cross_target.zig+3-3
...@@ -497,7 +497,7 @@ pub const CrossTarget = struct {...@@ -497,7 +497,7 @@ pub const CrossTarget = struct {
497497
498 pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![]u8 {498 pub fn zigTriple(self: CrossTarget, allocator: *mem.Allocator) error{OutOfMemory}![]u8 {
499 if (self.isNative()) {499 if (self.isNative()) {
500 return mem.dupe(allocator, u8, "native");500 return allocator.dupe(u8, "native");
501 }501 }
502502
503 const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";503 const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";
...@@ -514,14 +514,14 @@ pub const CrossTarget = struct {...@@ -514,14 +514,14 @@ pub const CrossTarget = struct {
514 switch (self.getOsVersionMin()) {514 switch (self.getOsVersionMin()) {
515 .none => {},515 .none => {},
516 .semver => |v| try result.outStream().print(".{}", .{v}),516 .semver => |v| try result.outStream().print(".{}", .{v}),
517 .windows => |v| try result.outStream().print(".{}", .{@tagName(v)}),517 .windows => |v| try result.outStream().print("{s}", .{v}),
518 }518 }
519 }519 }
520 if (self.os_version_max) |max| {520 if (self.os_version_max) |max| {
521 switch (max) {521 switch (max) {
522 .none => {},522 .none => {},
523 .semver => |v| try result.outStream().print("...{}", .{v}),523 .semver => |v| try result.outStream().print("...{}", .{v}),
524 .windows => |v| try result.outStream().print("...{}", .{@tagName(v)}),524 .windows => |v| try result.outStream().print("..{s}", .{v}),
525 }525 }
526 }526 }
527527
lib/std/zig/parse.zig+415-255
...@@ -150,7 +150,7 @@ const Parser = struct {...@@ -150,7 +150,7 @@ const Parser = struct {
150150
151 const visib_token = p.eatToken(.Keyword_pub);151 const visib_token = p.eatToken(.Keyword_pub);
152152
153 if (p.parseTopLevelDecl() catch |err| switch (err) {153 if (p.parseTopLevelDecl(doc_comments, visib_token) catch |err| switch (err) {
154 error.OutOfMemory => return error.OutOfMemory,154 error.OutOfMemory => return error.OutOfMemory,
155 error.ParseError => {155 error.ParseError => {
156 p.findNextContainerMember();156 p.findNextContainerMember();
...@@ -160,30 +160,7 @@ const Parser = struct {...@@ -160,30 +160,7 @@ const Parser = struct {
160 if (field_state == .seen) {160 if (field_state == .seen) {
161 field_state = .{ .end = visib_token orelse node.firstToken() };161 field_state = .{ .end = visib_token orelse node.firstToken() };
162 }162 }
163 switch (node.id) {
164 .FnProto => {
165 node.cast(Node.FnProto).?.doc_comments = doc_comments;
166 node.cast(Node.FnProto).?.visib_token = visib_token;
167 },
168 .VarDecl => {
169 node.cast(Node.VarDecl).?.doc_comments = doc_comments;
170 node.cast(Node.VarDecl).?.visib_token = visib_token;
171 },
172 .Use => {
173 node.cast(Node.Use).?.doc_comments = doc_comments;
174 node.cast(Node.Use).?.visib_token = visib_token;
175 },
176 else => unreachable,
177 }
178 try list.append(node);163 try list.append(node);
179 if (try p.parseAppendedDocComment(node.lastToken())) |appended_comment| {
180 switch (node.id) {
181 .FnProto => {},
182 .VarDecl => node.cast(Node.VarDecl).?.doc_comments = appended_comment,
183 .Use => node.cast(Node.Use).?.doc_comments = appended_comment,
184 else => unreachable,
185 }
186 }
187 continue;164 continue;
188 }165 }
189166
...@@ -417,7 +394,7 @@ const Parser = struct {...@@ -417,7 +394,7 @@ const Parser = struct {
417 /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)394 /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
418 /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl395 /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
419 /// / KEYWORD_usingnamespace Expr SEMICOLON396 /// / KEYWORD_usingnamespace Expr SEMICOLON
420 fn parseTopLevelDecl(p: *Parser) !?*Node {397 fn parseTopLevelDecl(p: *Parser, doc_comments: ?*Node.DocComment, visib_token: ?TokenIndex) !?*Node {
421 var lib_name: ?*Node = null;398 var lib_name: ?*Node = null;
422 const extern_export_inline_token = blk: {399 const extern_export_inline_token = blk: {
423 if (p.eatToken(.Keyword_export)) |token| break :blk token;400 if (p.eatToken(.Keyword_export)) |token| break :blk token;
...@@ -430,20 +407,12 @@ const Parser = struct {...@@ -430,20 +407,12 @@ const Parser = struct {
430 break :blk null;407 break :blk null;
431 };408 };
432409
433 if (try p.parseFnProto()) |node| {410 if (try p.parseFnProto(.top_level, .{
434 const fn_node = node.cast(Node.FnProto).?;411 .doc_comments = doc_comments,
435 fn_node.*.extern_export_inline_token = extern_export_inline_token;412 .visib_token = visib_token,
436 fn_node.*.lib_name = lib_name;413 .extern_export_inline_token = extern_export_inline_token,
437 if (p.eatToken(.Semicolon)) |_| return node;414 .lib_name = lib_name,
438415 })) |node| {
439 if (try p.expectNodeRecoverable(parseBlock, .{
440 // since parseBlock only return error.ParseError on
441 // a missing '}' we can assume this function was
442 // supposed to end here.
443 .ExpectedSemiOrLBrace = .{ .token = p.tok_i },
444 })) |body_node| {
445 fn_node.body_node = body_node;
446 }
447 return node;416 return node;
448 }417 }
449418
...@@ -460,12 +429,13 @@ const Parser = struct {...@@ -460,12 +429,13 @@ const Parser = struct {
460429
461 const thread_local_token = p.eatToken(.Keyword_threadlocal);430 const thread_local_token = p.eatToken(.Keyword_threadlocal);
462431
463 if (try p.parseVarDecl()) |node| {432 if (try p.parseVarDecl(.{
464 var var_decl = node.cast(Node.VarDecl).?;433 .doc_comments = doc_comments,
465 var_decl.*.thread_local_token = thread_local_token;434 .visib_token = visib_token,
466 var_decl.*.comptime_token = null;435 .thread_local_token = thread_local_token,
467 var_decl.*.extern_export_token = extern_export_inline_token;436 .extern_export_token = extern_export_inline_token,
468 var_decl.*.lib_name = lib_name;437 .lib_name = lib_name,
438 })) |node| {
469 return node;439 return node;
470 }440 }
471441
...@@ -485,21 +455,41 @@ const Parser = struct {...@@ -485,21 +455,41 @@ const Parser = struct {
485 return error.ParseError;455 return error.ParseError;
486 }456 }
487457
488 return p.parseUse();458 const use_token = p.eatToken(.Keyword_usingnamespace) orelse return null;
459 const expr = try p.expectNode(parseExpr, .{
460 .ExpectedExpr = .{ .token = p.tok_i },
461 });
462 const semicolon_token = try p.expectToken(.Semicolon);
463
464 const node = try p.arena.allocator.create(Node.Use);
465 node.* = .{
466 .doc_comments = doc_comments orelse try p.parseAppendedDocComment(semicolon_token),
467 .visib_token = visib_token,
468 .use_token = use_token,
469 .expr = expr,
470 .semicolon_token = semicolon_token,
471 };
472
473 return &node.base;
489 }474 }
490475
491 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)476 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)
492 fn parseFnProto(p: *Parser) !?*Node {477 fn parseFnProto(p: *Parser, level: enum { top_level, as_type }, fields: struct {
478 doc_comments: ?*Node.DocComment = null,
479 visib_token: ?TokenIndex = null,
480 extern_export_inline_token: ?TokenIndex = null,
481 lib_name: ?*Node = null,
482 }) !?*Node {
493 // TODO: Remove once extern/async fn rewriting is483 // TODO: Remove once extern/async fn rewriting is
494 var is_async = false;484 var is_async: ?void = null;
495 var is_extern = false;485 var is_extern_prototype: ?void = null;
496 const cc_token: ?TokenIndex = blk: {486 const cc_token: ?TokenIndex = blk: {
497 if (p.eatToken(.Keyword_extern)) |token| {487 if (p.eatToken(.Keyword_extern)) |token| {
498 is_extern = true;488 is_extern_prototype = {};
499 break :blk token;489 break :blk token;
500 }490 }
501 if (p.eatToken(.Keyword_async)) |token| {491 if (p.eatToken(.Keyword_async)) |token| {
502 is_async = true;492 is_async = {};
503 break :blk token;493 break :blk token;
504 }494 }
505 break :blk null;495 break :blk null;
...@@ -513,13 +503,14 @@ const Parser = struct {...@@ -513,13 +503,14 @@ const Parser = struct {
513 const lparen = try p.expectToken(.LParen);503 const lparen = try p.expectToken(.LParen);
514 const params = try p.parseParamDeclList();504 const params = try p.parseParamDeclList();
515 defer p.gpa.free(params);505 defer p.gpa.free(params);
506 const var_args_token = p.eatToken(.Ellipsis3);
516 const rparen = try p.expectToken(.RParen);507 const rparen = try p.expectToken(.RParen);
517 const align_expr = try p.parseByteAlign();508 const align_expr = try p.parseByteAlign();
518 const section_expr = try p.parseLinkSection();509 const section_expr = try p.parseLinkSection();
519 const callconv_expr = try p.parseCallconv();510 const callconv_expr = try p.parseCallconv();
520 const exclamation_token = p.eatToken(.Bang);511 const exclamation_token = p.eatToken(.Bang);
521512
522 const return_type_expr = (try p.parseVarType()) orelse513 const return_type_expr = (try p.parseAnyType()) orelse
523 try p.expectNodeRecoverable(parseTypeExpr, .{514 try p.expectNodeRecoverable(parseTypeExpr, .{
524 // most likely the user forgot to specify the return type.515 // most likely the user forgot to specify the return type.
525 // Mark return type as invalid and try to continue.516 // Mark return type as invalid and try to continue.
...@@ -535,37 +526,53 @@ const Parser = struct {...@@ -535,37 +526,53 @@ const Parser = struct {
535 else526 else
536 R{ .Explicit = return_type_expr.? };527 R{ .Explicit = return_type_expr.? };
537528
538 const var_args_token = if (params.len > 0) blk: {529 const body_node: ?*Node = switch (level) {
539 const param_type = params[params.len - 1].param_type;530 .top_level => blk: {
540 break :blk if (param_type == .var_args) param_type.var_args else null;531 if (p.eatToken(.Semicolon)) |_| {
541 } else532 break :blk null;
542 null;533 }
534 break :blk try p.expectNodeRecoverable(parseBlock, .{
535 // Since parseBlock only return error.ParseError on
536 // a missing '}' we can assume this function was
537 // supposed to end here.
538 .ExpectedSemiOrLBrace = .{ .token = p.tok_i },
539 });
540 },
541 .as_type => null,
542 };
543543
544 const fn_proto_node = try Node.FnProto.alloc(&p.arena.allocator, params.len);544 const fn_proto_node = try Node.FnProto.create(&p.arena.allocator, .{
545 fn_proto_node.* = .{
546 .doc_comments = null,
547 .visib_token = null,
548 .fn_token = fn_token,
549 .name_token = name_token,
550 .params_len = params.len,545 .params_len = params.len,
546 .fn_token = fn_token,
551 .return_type = return_type,547 .return_type = return_type,
548 }, .{
549 .doc_comments = fields.doc_comments,
550 .visib_token = fields.visib_token,
551 .name_token = name_token,
552 .var_args_token = var_args_token,552 .var_args_token = var_args_token,
553 .extern_export_inline_token = null,553 .extern_export_inline_token = fields.extern_export_inline_token,
554 .body_node = null,554 .body_node = body_node,
555 .lib_name = null,555 .lib_name = fields.lib_name,
556 .align_expr = align_expr,556 .align_expr = align_expr,
557 .section_expr = section_expr,557 .section_expr = section_expr,
558 .callconv_expr = callconv_expr,558 .callconv_expr = callconv_expr,
559 .is_extern_prototype = is_extern,559 .is_extern_prototype = is_extern_prototype,
560 .is_async = is_async,560 .is_async = is_async,
561 };561 });
562 std.mem.copy(Node.FnProto.ParamDecl, fn_proto_node.params(), params);562 std.mem.copy(Node.FnProto.ParamDecl, fn_proto_node.params(), params);
563563
564 return &fn_proto_node.base;564 return &fn_proto_node.base;
565 }565 }
566566
567 /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON567 /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
568 fn parseVarDecl(p: *Parser) !?*Node {568 fn parseVarDecl(p: *Parser, fields: struct {
569 doc_comments: ?*Node.DocComment = null,
570 visib_token: ?TokenIndex = null,
571 thread_local_token: ?TokenIndex = null,
572 extern_export_token: ?TokenIndex = null,
573 lib_name: ?*Node = null,
574 comptime_token: ?TokenIndex = null,
575 }) !?*Node {
569 const mut_token = p.eatToken(.Keyword_const) orelse576 const mut_token = p.eatToken(.Keyword_const) orelse
570 p.eatToken(.Keyword_var) orelse577 p.eatToken(.Keyword_var) orelse
571 return null;578 return null;
...@@ -587,23 +594,25 @@ const Parser = struct {...@@ -587,23 +594,25 @@ const Parser = struct {
587 } else null;594 } else null;
588 const semicolon_token = try p.expectToken(.Semicolon);595 const semicolon_token = try p.expectToken(.Semicolon);
589596
590 const node = try p.arena.allocator.create(Node.VarDecl);597 const doc_comments = fields.doc_comments orelse try p.parseAppendedDocComment(semicolon_token);
591 node.* = .{598
592 .doc_comments = null,599 const node = try Node.VarDecl.create(&p.arena.allocator, .{
593 .visib_token = null,600 .mut_token = mut_token,
594 .thread_local_token = null,
595 .name_token = name_token,601 .name_token = name_token,
602 .semicolon_token = semicolon_token,
603 }, .{
604 .doc_comments = doc_comments,
605 .visib_token = fields.visib_token,
606 .thread_local_token = fields.thread_local_token,
596 .eq_token = eq_token,607 .eq_token = eq_token,
597 .mut_token = mut_token,608 .comptime_token = fields.comptime_token,
598 .comptime_token = null,609 .extern_export_token = fields.extern_export_token,
599 .extern_export_token = null,610 .lib_name = fields.lib_name,
600 .lib_name = null,
601 .type_node = type_node,611 .type_node = type_node,
602 .align_node = align_node,612 .align_node = align_node,
603 .section_node = section_node,613 .section_node = section_node,
604 .init_node = init_node,614 .init_node = init_node,
605 .semicolon_token = semicolon_token,615 });
606 };
607 return &node.base;616 return &node.base;
608 }617 }
609618
...@@ -618,9 +627,9 @@ const Parser = struct {...@@ -618,9 +627,9 @@ const Parser = struct {
618 var align_expr: ?*Node = null;627 var align_expr: ?*Node = null;
619 var type_expr: ?*Node = null;628 var type_expr: ?*Node = null;
620 if (p.eatToken(.Colon)) |_| {629 if (p.eatToken(.Colon)) |_| {
621 if (p.eatToken(.Keyword_var)) |var_tok| {630 if (p.eatToken(.Keyword_anytype) orelse p.eatToken(.Keyword_var)) |anytype_tok| {
622 const node = try p.arena.allocator.create(Node.VarType);631 const node = try p.arena.allocator.create(Node.AnyType);
623 node.* = .{ .token = var_tok };632 node.* = .{ .token = anytype_tok };
624 type_expr = &node.base;633 type_expr = &node.base;
625 } else {634 } else {
626 type_expr = try p.expectNode(parseTypeExpr, .{635 type_expr = try p.expectNode(parseTypeExpr, .{
...@@ -663,10 +672,9 @@ const Parser = struct {...@@ -663,10 +672,9 @@ const Parser = struct {
663 fn parseStatement(p: *Parser) Error!?*Node {672 fn parseStatement(p: *Parser) Error!?*Node {
664 const comptime_token = p.eatToken(.Keyword_comptime);673 const comptime_token = p.eatToken(.Keyword_comptime);
665674
666 const var_decl_node = try p.parseVarDecl();675 if (try p.parseVarDecl(.{
667 if (var_decl_node) |node| {676 .comptime_token = comptime_token,
668 const var_decl = node.cast(Node.VarDecl).?;677 })) |node| {
669 var_decl.comptime_token = comptime_token;
670 return node;678 return node;
671 }679 }
672680
...@@ -937,7 +945,6 @@ const Parser = struct {...@@ -937,7 +945,6 @@ const Parser = struct {
937 return node;945 return node;
938 }946 }
939947
940
941 while_prefix.body = try p.expectNode(parseAssignExpr, .{948 while_prefix.body = try p.expectNode(parseAssignExpr, .{
942 .ExpectedBlockOrAssignment = .{ .token = p.tok_i },949 .ExpectedBlockOrAssignment = .{ .token = p.tok_i },
943 });950 });
...@@ -1008,7 +1015,7 @@ const Parser = struct {...@@ -1008,7 +1015,7 @@ const Parser = struct {
1008 /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*1015 /// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
1009 fn parseBoolOrExpr(p: *Parser) !?*Node {1016 fn parseBoolOrExpr(p: *Parser) !?*Node {
1010 return p.parseBinOpExpr(1017 return p.parseBinOpExpr(
1011 SimpleBinOpParseFn(.Keyword_or, Node.InfixOp.Op.BoolOr),1018 SimpleBinOpParseFn(.Keyword_or, .BoolOr),
1012 parseBoolAndExpr,1019 parseBoolAndExpr,
1013 .Infinitely,1020 .Infinitely,
1014 );1021 );
...@@ -1121,10 +1128,10 @@ const Parser = struct {...@@ -1121,10 +1128,10 @@ const Parser = struct {
1121 const expr_node = try p.expectNode(parseExpr, .{1128 const expr_node = try p.expectNode(parseExpr, .{
1122 .ExpectedExpr = .{ .token = p.tok_i },1129 .ExpectedExpr = .{ .token = p.tok_i },
1123 });1130 });
1124 const node = try p.arena.allocator.create(Node.PrefixOp);1131 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
1125 node.* = .{1132 node.* = .{
1133 .base = .{ .tag = .Resume },
1126 .op_token = token,1134 .op_token = token,
1127 .op = .Resume,
1128 .rhs = expr_node,1135 .rhs = expr_node,
1129 };1136 };
1130 return &node.base;1137 return &node.base;
...@@ -1398,8 +1405,8 @@ const Parser = struct {...@@ -1398,8 +1405,8 @@ const Parser = struct {
1398 fn parseErrorUnionExpr(p: *Parser) !?*Node {1405 fn parseErrorUnionExpr(p: *Parser) !?*Node {
1399 const suffix_expr = (try p.parseSuffixExpr()) orelse return null;1406 const suffix_expr = (try p.parseSuffixExpr()) orelse return null;
14001407
1401 if (try SimpleBinOpParseFn(.Bang, Node.InfixOp.Op.ErrorUnion)(p)) |node| {1408 if (try SimpleBinOpParseFn(.Bang, .ErrorUnion)(p)) |node| {
1402 const error_union = node.cast(Node.InfixOp).?;1409 const error_union = node.castTag(.ErrorUnion).?;
1403 const type_expr = try p.expectNode(parseTypeExpr, .{1410 const type_expr = try p.expectNode(parseTypeExpr, .{
1404 .ExpectedTypeExpr = .{ .token = p.tok_i },1411 .ExpectedTypeExpr = .{ .token = p.tok_i },
1405 });1412 });
...@@ -1432,10 +1439,56 @@ const Parser = struct {...@@ -1432,10 +1439,56 @@ const Parser = struct {
1432 .ExpectedPrimaryTypeExpr = .{ .token = p.tok_i },1439 .ExpectedPrimaryTypeExpr = .{ .token = p.tok_i },
1433 });1440 });
14341441
1442 // TODO pass `res` into `parseSuffixOp` rather than patching it up afterwards.
1435 while (try p.parseSuffixOp()) |node| {1443 while (try p.parseSuffixOp()) |node| {
1436 switch (node.id) {1444 switch (node.tag) {
1437 .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,1445 .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
1438 .InfixOp => node.cast(Node.InfixOp).?.lhs = res,1446 .Catch => node.castTag(.Catch).?.lhs = res,
1447
1448 .Add,
1449 .AddWrap,
1450 .ArrayCat,
1451 .ArrayMult,
1452 .Assign,
1453 .AssignBitAnd,
1454 .AssignBitOr,
1455 .AssignBitShiftLeft,
1456 .AssignBitShiftRight,
1457 .AssignBitXor,
1458 .AssignDiv,
1459 .AssignSub,
1460 .AssignSubWrap,
1461 .AssignMod,
1462 .AssignAdd,
1463 .AssignAddWrap,
1464 .AssignMul,
1465 .AssignMulWrap,
1466 .BangEqual,
1467 .BitAnd,
1468 .BitOr,
1469 .BitShiftLeft,
1470 .BitShiftRight,
1471 .BitXor,
1472 .BoolAnd,
1473 .BoolOr,
1474 .Div,
1475 .EqualEqual,
1476 .ErrorUnion,
1477 .GreaterOrEqual,
1478 .GreaterThan,
1479 .LessOrEqual,
1480 .LessThan,
1481 .MergeErrorSets,
1482 .Mod,
1483 .Mul,
1484 .MulWrap,
1485 .Period,
1486 .Range,
1487 .Sub,
1488 .SubWrap,
1489 .UnwrapOptional,
1490 => node.cast(Node.SimpleInfixOp).?.lhs = res,
1491
1439 else => unreachable,1492 else => unreachable,
1440 }1493 }
1441 res = node;1494 res = node;
...@@ -1463,10 +1516,55 @@ const Parser = struct {...@@ -1463,10 +1516,55 @@ const Parser = struct {
1463 var res = expr;1516 var res = expr;
14641517
1465 while (true) {1518 while (true) {
1519 // TODO pass `res` into `parseSuffixOp` rather than patching it up afterwards.
1466 if (try p.parseSuffixOp()) |node| {1520 if (try p.parseSuffixOp()) |node| {
1467 switch (node.id) {1521 switch (node.tag) {
1468 .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,1522 .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
1469 .InfixOp => node.cast(Node.InfixOp).?.lhs = res,1523 .Catch => node.castTag(.Catch).?.lhs = res,
1524
1525 .Add,
1526 .AddWrap,
1527 .ArrayCat,
1528 .ArrayMult,
1529 .Assign,
1530 .AssignBitAnd,
1531 .AssignBitOr,
1532 .AssignBitShiftLeft,
1533 .AssignBitShiftRight,
1534 .AssignBitXor,
1535 .AssignDiv,
1536 .AssignSub,
1537 .AssignSubWrap,
1538 .AssignMod,
1539 .AssignAdd,
1540 .AssignAddWrap,
1541 .AssignMul,
1542 .AssignMulWrap,
1543 .BangEqual,
1544 .BitAnd,
1545 .BitOr,
1546 .BitShiftLeft,
1547 .BitShiftRight,
1548 .BitXor,
1549 .BoolAnd,
1550 .BoolOr,
1551 .Div,
1552 .EqualEqual,
1553 .ErrorUnion,
1554 .GreaterOrEqual,
1555 .GreaterThan,
1556 .LessOrEqual,
1557 .LessThan,
1558 .MergeErrorSets,
1559 .Mod,
1560 .Mul,
1561 .MulWrap,
1562 .Period,
1563 .Range,
1564 .Sub,
1565 .SubWrap,
1566 .UnwrapOptional,
1567 => node.cast(Node.SimpleInfixOp).?.lhs = res,
1470 else => unreachable,1568 else => unreachable,
1471 }1569 }
1472 res = node;1570 res = node;
...@@ -1529,7 +1627,7 @@ const Parser = struct {...@@ -1529,7 +1627,7 @@ const Parser = struct {
1529 if (try p.parseAnonLiteral()) |node| return node;1627 if (try p.parseAnonLiteral()) |node| return node;
1530 if (try p.parseErrorSetDecl()) |node| return node;1628 if (try p.parseErrorSetDecl()) |node| return node;
1531 if (try p.parseFloatLiteral()) |node| return node;1629 if (try p.parseFloatLiteral()) |node| return node;
1532 if (try p.parseFnProto()) |node| return node;1630 if (try p.parseFnProto(.as_type, .{})) |node| return node;
1533 if (try p.parseGroupedExpr()) |node| return node;1631 if (try p.parseGroupedExpr()) |node| return node;
1534 if (try p.parseLabeledTypeExpr()) |node| return node;1632 if (try p.parseLabeledTypeExpr()) |node| return node;
1535 if (try p.parseIdentifier()) |node| return node;1633 if (try p.parseIdentifier()) |node| return node;
...@@ -1553,11 +1651,11 @@ const Parser = struct {...@@ -1553,11 +1651,11 @@ const Parser = struct {
1553 const global_error_set = try p.createLiteral(Node.ErrorType, token);1651 const global_error_set = try p.createLiteral(Node.ErrorType, token);
1554 if (period == null or identifier == null) return global_error_set;1652 if (period == null or identifier == null) return global_error_set;
15551653
1556 const node = try p.arena.allocator.create(Node.InfixOp);1654 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
1557 node.* = .{1655 node.* = .{
1656 .base = Node{ .tag = .Period },
1558 .op_token = period.?,1657 .op_token = period.?,
1559 .lhs = global_error_set,1658 .lhs = global_error_set,
1560 .op = .Period,
1561 .rhs = identifier.?,1659 .rhs = identifier.?,
1562 };1660 };
1563 return &node.base;1661 return &node.base;
...@@ -1654,7 +1752,7 @@ const Parser = struct {...@@ -1654,7 +1752,7 @@ const Parser = struct {
1654 }1752 }
16551753
1656 if (try p.parseLoopTypeExpr()) |node| {1754 if (try p.parseLoopTypeExpr()) |node| {
1657 switch (node.id) {1755 switch (node.tag) {
1658 .For => node.cast(Node.For).?.label = label,1756 .For => node.cast(Node.For).?.label = label,
1659 .While => node.cast(Node.While).?.label = label,1757 .While => node.cast(Node.While).?.label = label,
1660 else => unreachable,1758 else => unreachable,
...@@ -2023,14 +2121,13 @@ const Parser = struct {...@@ -2023,14 +2121,13 @@ const Parser = struct {
2023 }2121 }
20242122
2025 /// ParamType2123 /// ParamType
2026 /// <- KEYWORD_var2124 /// <- Keyword_anytype
2027 /// / DOT32125 /// / DOT3
2028 /// / TypeExpr2126 /// / TypeExpr
2029 fn parseParamType(p: *Parser) !?Node.FnProto.ParamDecl.ParamType {2127 fn parseParamType(p: *Parser) !?Node.FnProto.ParamDecl.ParamType {
2030 // TODO cast from tuple to error union is broken2128 // TODO cast from tuple to error union is broken
2031 const P = Node.FnProto.ParamDecl.ParamType;2129 const P = Node.FnProto.ParamDecl.ParamType;
2032 if (try p.parseVarType()) |node| return P{ .var_type = node };2130 if (try p.parseAnyType()) |node| return P{ .any_type = node };
2033 if (p.eatToken(.Ellipsis3)) |token| return P{ .var_args = token };
2034 if (try p.parseTypeExpr()) |node| return P{ .type_expr = node };2131 if (try p.parseTypeExpr()) |node| return P{ .type_expr = node };
2035 return null;2132 return null;
2036 }2133 }
...@@ -2231,11 +2328,11 @@ const Parser = struct {...@@ -2231,11 +2328,11 @@ const Parser = struct {
2231 .ExpectedExpr = .{ .token = p.tok_i },2328 .ExpectedExpr = .{ .token = p.tok_i },
2232 });2329 });
22332330
2234 const node = try p.arena.allocator.create(Node.InfixOp);2331 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
2235 node.* = .{2332 node.* = .{
2333 .base = Node{ .tag = .Range },
2236 .op_token = token,2334 .op_token = token,
2237 .lhs = expr,2335 .lhs = expr,
2238 .op = .Range,
2239 .rhs = range_end,2336 .rhs = range_end,
2240 };2337 };
2241 return &node.base;2338 return &node.base;
...@@ -2260,7 +2357,7 @@ const Parser = struct {...@@ -2260,7 +2357,7 @@ const Parser = struct {
2260 /// / EQUAL2357 /// / EQUAL
2261 fn parseAssignOp(p: *Parser) !?*Node {2358 fn parseAssignOp(p: *Parser) !?*Node {
2262 const token = p.nextToken();2359 const token = p.nextToken();
2263 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {2360 const op: Node.Tag = switch (p.token_ids[token]) {
2264 .AsteriskEqual => .AssignMul,2361 .AsteriskEqual => .AssignMul,
2265 .SlashEqual => .AssignDiv,2362 .SlashEqual => .AssignDiv,
2266 .PercentEqual => .AssignMod,2363 .PercentEqual => .AssignMod,
...@@ -2281,11 +2378,11 @@ const Parser = struct {...@@ -2281,11 +2378,11 @@ const Parser = struct {
2281 },2378 },
2282 };2379 };
22832380
2284 const node = try p.arena.allocator.create(Node.InfixOp);2381 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
2285 node.* = .{2382 node.* = .{
2383 .base = .{ .tag = op },
2286 .op_token = token,2384 .op_token = token,
2287 .lhs = undefined, // set by caller2385 .lhs = undefined, // set by caller
2288 .op = op,
2289 .rhs = undefined, // set by caller2386 .rhs = undefined, // set by caller
2290 };2387 };
2291 return &node.base;2388 return &node.base;
...@@ -2300,7 +2397,7 @@ const Parser = struct {...@@ -2300,7 +2397,7 @@ const Parser = struct {
2300 /// / RARROWEQUAL2397 /// / RARROWEQUAL
2301 fn parseCompareOp(p: *Parser) !?*Node {2398 fn parseCompareOp(p: *Parser) !?*Node {
2302 const token = p.nextToken();2399 const token = p.nextToken();
2303 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {2400 const op: Node.Tag = switch (p.token_ids[token]) {
2304 .EqualEqual => .EqualEqual,2401 .EqualEqual => .EqualEqual,
2305 .BangEqual => .BangEqual,2402 .BangEqual => .BangEqual,
2306 .AngleBracketLeft => .LessThan,2403 .AngleBracketLeft => .LessThan,
...@@ -2324,12 +2421,22 @@ const Parser = struct {...@@ -2324,12 +2421,22 @@ const Parser = struct {
2324 /// / KEYWORD_catch Payload?2421 /// / KEYWORD_catch Payload?
2325 fn parseBitwiseOp(p: *Parser) !?*Node {2422 fn parseBitwiseOp(p: *Parser) !?*Node {
2326 const token = p.nextToken();2423 const token = p.nextToken();
2327 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {2424 const op: Node.Tag = switch (p.token_ids[token]) {
2328 .Ampersand => .BitAnd,2425 .Ampersand => .BitAnd,
2329 .Caret => .BitXor,2426 .Caret => .BitXor,
2330 .Pipe => .BitOr,2427 .Pipe => .BitOr,
2331 .Keyword_orelse => .UnwrapOptional,2428 .Keyword_orelse => .UnwrapOptional,
2332 .Keyword_catch => .{ .Catch = try p.parsePayload() },2429 .Keyword_catch => {
2430 const payload = try p.parsePayload();
2431 const node = try p.arena.allocator.create(Node.Catch);
2432 node.* = .{
2433 .op_token = token,
2434 .lhs = undefined, // set by caller
2435 .rhs = undefined, // set by caller
2436 .payload = payload,
2437 };
2438 return &node.base;
2439 },
2333 else => {2440 else => {
2334 p.putBackToken(token);2441 p.putBackToken(token);
2335 return null;2442 return null;
...@@ -2344,7 +2451,7 @@ const Parser = struct {...@@ -2344,7 +2451,7 @@ const Parser = struct {
2344 /// / RARROW22451 /// / RARROW2
2345 fn parseBitShiftOp(p: *Parser) !?*Node {2452 fn parseBitShiftOp(p: *Parser) !?*Node {
2346 const token = p.nextToken();2453 const token = p.nextToken();
2347 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {2454 const op: Node.Tag = switch (p.token_ids[token]) {
2348 .AngleBracketAngleBracketLeft => .BitShiftLeft,2455 .AngleBracketAngleBracketLeft => .BitShiftLeft,
2349 .AngleBracketAngleBracketRight => .BitShiftRight,2456 .AngleBracketAngleBracketRight => .BitShiftRight,
2350 else => {2457 else => {
...@@ -2364,7 +2471,7 @@ const Parser = struct {...@@ -2364,7 +2471,7 @@ const Parser = struct {
2364 /// / MINUSPERCENT2471 /// / MINUSPERCENT
2365 fn parseAdditionOp(p: *Parser) !?*Node {2472 fn parseAdditionOp(p: *Parser) !?*Node {
2366 const token = p.nextToken();2473 const token = p.nextToken();
2367 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {2474 const op: Node.Tag = switch (p.token_ids[token]) {
2368 .Plus => .Add,2475 .Plus => .Add,
2369 .Minus => .Sub,2476 .Minus => .Sub,
2370 .PlusPlus => .ArrayCat,2477 .PlusPlus => .ArrayCat,
...@@ -2388,7 +2495,7 @@ const Parser = struct {...@@ -2388,7 +2495,7 @@ const Parser = struct {
2388 /// / ASTERISKPERCENT2495 /// / ASTERISKPERCENT
2389 fn parseMultiplyOp(p: *Parser) !?*Node {2496 fn parseMultiplyOp(p: *Parser) !?*Node {
2390 const token = p.nextToken();2497 const token = p.nextToken();
2391 const op: Node.InfixOp.Op = switch (p.token_ids[token]) {2498 const op: Node.Tag = switch (p.token_ids[token]) {
2392 .PipePipe => .MergeErrorSets,2499 .PipePipe => .MergeErrorSets,
2393 .Asterisk => .Mul,2500 .Asterisk => .Mul,
2394 .Slash => .Div,2501 .Slash => .Div,
...@@ -2414,24 +2521,26 @@ const Parser = struct {...@@ -2414,24 +2521,26 @@ const Parser = struct {
2414 /// / KEYWORD_await2521 /// / KEYWORD_await
2415 fn parsePrefixOp(p: *Parser) !?*Node {2522 fn parsePrefixOp(p: *Parser) !?*Node {
2416 const token = p.nextToken();2523 const token = p.nextToken();
2417 const op: Node.PrefixOp.Op = switch (p.token_ids[token]) {2524 switch (p.token_ids[token]) {
2418 .Bang => .BoolNot,2525 .Bang => return p.allocSimplePrefixOp(.BoolNot, token),
2419 .Minus => .Negation,2526 .Minus => return p.allocSimplePrefixOp(.Negation, token),
2420 .Tilde => .BitNot,2527 .Tilde => return p.allocSimplePrefixOp(.BitNot, token),
2421 .MinusPercent => .NegationWrap,2528 .MinusPercent => return p.allocSimplePrefixOp(.NegationWrap, token),
2422 .Ampersand => .AddressOf,2529 .Ampersand => return p.allocSimplePrefixOp(.AddressOf, token),
2423 .Keyword_try => .Try,2530 .Keyword_try => return p.allocSimplePrefixOp(.Try, token),
2424 .Keyword_await => .Await,2531 .Keyword_await => return p.allocSimplePrefixOp(.Await, token),
2425 else => {2532 else => {
2426 p.putBackToken(token);2533 p.putBackToken(token);
2427 return null;2534 return null;
2428 },2535 },
2429 };2536 }
2537 }
24302538
2431 const node = try p.arena.allocator.create(Node.PrefixOp);2539 fn allocSimplePrefixOp(p: *Parser, comptime tag: Node.Tag, token: TokenIndex) !?*Node {
2540 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
2432 node.* = .{2541 node.* = .{
2542 .base = .{ .tag = tag },
2433 .op_token = token,2543 .op_token = token,
2434 .op = op,
2435 .rhs = undefined, // set by caller2544 .rhs = undefined, // set by caller
2436 };2545 };
2437 return &node.base;2546 return &node.base;
...@@ -2451,19 +2560,15 @@ const Parser = struct {...@@ -2451,19 +2560,15 @@ const Parser = struct {
2451 /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*2560 /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
2452 fn parsePrefixTypeOp(p: *Parser) !?*Node {2561 fn parsePrefixTypeOp(p: *Parser) !?*Node {
2453 if (p.eatToken(.QuestionMark)) |token| {2562 if (p.eatToken(.QuestionMark)) |token| {
2454 const node = try p.arena.allocator.create(Node.PrefixOp);2563 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
2455 node.* = .{2564 node.* = .{
2565 .base = .{ .tag = .OptionalType },
2456 .op_token = token,2566 .op_token = token,
2457 .op = .OptionalType,
2458 .rhs = undefined, // set by caller2567 .rhs = undefined, // set by caller
2459 };2568 };
2460 return &node.base;2569 return &node.base;
2461 }2570 }
24622571
2463 // TODO: Returning a AnyFrameType instead of PrefixOp makes casting and setting .rhs or
2464 // .return_type more difficult for the caller (see parsePrefixOpExpr helper).
2465 // Consider making the AnyFrameType a member of PrefixOp and add a
2466 // PrefixOp.AnyFrameType variant?
2467 if (p.eatToken(.Keyword_anyframe)) |token| {2572 if (p.eatToken(.Keyword_anyframe)) |token| {
2468 const arrow = p.eatToken(.Arrow) orelse {2573 const arrow = p.eatToken(.Arrow) orelse {
2469 p.putBackToken(token);2574 p.putBackToken(token);
...@@ -2483,11 +2588,15 @@ const Parser = struct {...@@ -2483,11 +2588,15 @@ const Parser = struct {
2483 if (try p.parsePtrTypeStart()) |node| {2588 if (try p.parsePtrTypeStart()) |node| {
2484 // If the token encountered was **, there will be two nodes instead of one.2589 // If the token encountered was **, there will be two nodes instead of one.
2485 // The attributes should be applied to the rightmost operator.2590 // The attributes should be applied to the rightmost operator.
2486 const prefix_op = node.cast(Node.PrefixOp).?;2591 var ptr_info = if (node.cast(Node.PtrType)) |ptr_type|
2487 var ptr_info = if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk)2592 if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk)
2488 &prefix_op.rhs.cast(Node.PrefixOp).?.op.PtrType2593 &ptr_type.rhs.cast(Node.PtrType).?.ptr_info
2594 else
2595 &ptr_type.ptr_info
2596 else if (node.cast(Node.SliceType)) |slice_type|
2597 &slice_type.ptr_info
2489 else2598 else
2490 &prefix_op.op.PtrType;2599 unreachable;
24912600
2492 while (true) {2601 while (true) {
2493 if (p.eatToken(.Keyword_align)) |align_token| {2602 if (p.eatToken(.Keyword_align)) |align_token| {
...@@ -2506,7 +2615,7 @@ const Parser = struct {...@@ -2506,7 +2615,7 @@ const Parser = struct {
2506 .ExpectedIntegerLiteral = .{ .token = p.tok_i },2615 .ExpectedIntegerLiteral = .{ .token = p.tok_i },
2507 });2616 });
25082617
2509 break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{2618 break :bit_range_value ast.PtrInfo.Align.BitRange{
2510 .start = range_start,2619 .start = range_start,
2511 .end = range_end,2620 .end = range_end,
2512 };2621 };
...@@ -2520,7 +2629,7 @@ const Parser = struct {...@@ -2520,7 +2629,7 @@ const Parser = struct {
2520 continue;2629 continue;
2521 }2630 }
25222631
2523 ptr_info.align_info = Node.PrefixOp.PtrInfo.Align{2632 ptr_info.align_info = ast.PtrInfo.Align{
2524 .node = expr_node,2633 .node = expr_node,
2525 .bit_range = bit_range,2634 .bit_range = bit_range,
2526 };2635 };
...@@ -2564,58 +2673,54 @@ const Parser = struct {...@@ -2564,58 +2673,54 @@ const Parser = struct {
2564 }2673 }
25652674
2566 if (try p.parseArrayTypeStart()) |node| {2675 if (try p.parseArrayTypeStart()) |node| {
2567 switch (node.cast(Node.PrefixOp).?.op) {2676 if (node.cast(Node.SliceType)) |slice_type| {
2568 .ArrayType => {},2677 // Collect pointer qualifiers in any order, but disallow duplicates
2569 .SliceType => |*slice_type| {2678 while (true) {
2570 // Collect pointer qualifiers in any order, but disallow duplicates2679 if (try p.parseByteAlign()) |align_expr| {
2571 while (true) {2680 if (slice_type.ptr_info.align_info != null) {
2572 if (try p.parseByteAlign()) |align_expr| {2681 try p.errors.append(p.gpa, .{
2573 if (slice_type.align_info != null) {2682 .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
2574 try p.errors.append(p.gpa, .{2683 });
2575 .ExtraAlignQualifier = .{ .token = p.tok_i - 1 },
2576 });
2577 continue;
2578 }
2579 slice_type.align_info = Node.PrefixOp.PtrInfo.Align{
2580 .node = align_expr,
2581 .bit_range = null,
2582 };
2583 continue;2684 continue;
2584 }2685 }
2585 if (p.eatToken(.Keyword_const)) |const_token| {2686 slice_type.ptr_info.align_info = ast.PtrInfo.Align{
2586 if (slice_type.const_token != null) {2687 .node = align_expr,
2587 try p.errors.append(p.gpa, .{2688 .bit_range = null,
2588 .ExtraConstQualifier = .{ .token = p.tok_i - 1 },2689 };
2589 });2690 continue;
2590 continue;2691 }
2591 }2692 if (p.eatToken(.Keyword_const)) |const_token| {
2592 slice_type.const_token = const_token;2693 if (slice_type.ptr_info.const_token != null) {
2694 try p.errors.append(p.gpa, .{
2695 .ExtraConstQualifier = .{ .token = p.tok_i - 1 },
2696 });
2593 continue;2697 continue;
2594 }2698 }
2595 if (p.eatToken(.Keyword_volatile)) |volatile_token| {2699 slice_type.ptr_info.const_token = const_token;
2596 if (slice_type.volatile_token != null) {2700 continue;
2597 try p.errors.append(p.gpa, .{2701 }
2598 .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },2702 if (p.eatToken(.Keyword_volatile)) |volatile_token| {
2599 });2703 if (slice_type.ptr_info.volatile_token != null) {
2600 continue;2704 try p.errors.append(p.gpa, .{
2601 }2705 .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 },
2602 slice_type.volatile_token = volatile_token;2706 });
2603 continue;2707 continue;
2604 }2708 }
2605 if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {2709 slice_type.ptr_info.volatile_token = volatile_token;
2606 if (slice_type.allowzero_token != null) {2710 continue;
2607 try p.errors.append(p.gpa, .{2711 }
2608 .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },2712 if (p.eatToken(.Keyword_allowzero)) |allowzero_token| {
2609 });2713 if (slice_type.ptr_info.allowzero_token != null) {
2610 continue;2714 try p.errors.append(p.gpa, .{
2611 }2715 .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 },
2612 slice_type.allowzero_token = allowzero_token;2716 });
2613 continue;2717 continue;
2614 }2718 }
2615 break;2719 slice_type.ptr_info.allowzero_token = allowzero_token;
2720 continue;
2616 }2721 }
2617 },2722 break;
2618 else => unreachable,2723 }
2619 }2724 }
2620 return node;2725 return node;
2621 }2726 }
...@@ -2669,14 +2774,14 @@ const Parser = struct {...@@ -2669,14 +2774,14 @@ const Parser = struct {
26692774
2670 if (p.eatToken(.Period)) |period| {2775 if (p.eatToken(.Period)) |period| {
2671 if (try p.parseIdentifier()) |identifier| {2776 if (try p.parseIdentifier()) |identifier| {
2672 // TODO: It's a bit weird to return an InfixOp from the SuffixOp parser.2777 // TODO: It's a bit weird to return a SimpleInfixOp from the SuffixOp parser.
2673 // Should there be an Node.SuffixOp.FieldAccess variant? Or should2778 // Should there be an Node.SuffixOp.FieldAccess variant? Or should
2674 // this grammar rule be altered?2779 // this grammar rule be altered?
2675 const node = try p.arena.allocator.create(Node.InfixOp);2780 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
2676 node.* = .{2781 node.* = .{
2782 .base = Node{ .tag = .Period },
2677 .op_token = period,2783 .op_token = period,
2678 .lhs = undefined, // set by caller2784 .lhs = undefined, // set by caller
2679 .op = .Period,
2680 .rhs = identifier,2785 .rhs = identifier,
2681 };2786 };
2682 return &node.base;2787 return &node.base;
...@@ -2729,29 +2834,32 @@ const Parser = struct {...@@ -2729,29 +2834,32 @@ const Parser = struct {
2729 null;2834 null;
2730 const rbracket = try p.expectToken(.RBracket);2835 const rbracket = try p.expectToken(.RBracket);
27312836
2732 const op: Node.PrefixOp.Op = if (expr) |len_expr|2837 if (expr) |len_expr| {
2733 .{2838 if (sentinel) |s| {
2734 .ArrayType = .{2839 const node = try p.arena.allocator.create(Node.ArrayTypeSentinel);
2840 node.* = .{
2841 .op_token = lbracket,
2842 .rhs = undefined, // set by caller
2735 .len_expr = len_expr,2843 .len_expr = len_expr,
2736 .sentinel = sentinel,2844 .sentinel = s,
2737 },2845 };
2846 return &node.base;
2847 } else {
2848 const node = try p.arena.allocator.create(Node.ArrayType);
2849 node.* = .{
2850 .op_token = lbracket,
2851 .rhs = undefined, // set by caller
2852 .len_expr = len_expr,
2853 };
2854 return &node.base;
2738 }2855 }
2739 else2856 }
2740 .{
2741 .SliceType = Node.PrefixOp.PtrInfo{
2742 .allowzero_token = null,
2743 .align_info = null,
2744 .const_token = null,
2745 .volatile_token = null,
2746 .sentinel = sentinel,
2747 },
2748 };
27492857
2750 const node = try p.arena.allocator.create(Node.PrefixOp);2858 const node = try p.arena.allocator.create(Node.SliceType);
2751 node.* = .{2859 node.* = .{
2752 .op_token = lbracket,2860 .op_token = lbracket,
2753 .op = op,
2754 .rhs = undefined, // set by caller2861 .rhs = undefined, // set by caller
2862 .ptr_info = .{ .sentinel = sentinel },
2755 };2863 };
2756 return &node.base;2864 return &node.base;
2757 }2865 }
...@@ -2769,28 +2877,26 @@ const Parser = struct {...@@ -2769,28 +2877,26 @@ const Parser = struct {
2769 })2877 })
2770 else2878 else
2771 null;2879 null;
2772 const node = try p.arena.allocator.create(Node.PrefixOp);2880 const node = try p.arena.allocator.create(Node.PtrType);
2773 node.* = .{2881 node.* = .{
2774 .op_token = asterisk,2882 .op_token = asterisk,
2775 .op = .{ .PtrType = .{ .sentinel = sentinel } },
2776 .rhs = undefined, // set by caller2883 .rhs = undefined, // set by caller
2884 .ptr_info = .{ .sentinel = sentinel },
2777 };2885 };
2778 return &node.base;2886 return &node.base;
2779 }2887 }
27802888
2781 if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| {2889 if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| {
2782 const node = try p.arena.allocator.create(Node.PrefixOp);2890 const node = try p.arena.allocator.create(Node.PtrType);
2783 node.* = .{2891 node.* = .{
2784 .op_token = double_asterisk,2892 .op_token = double_asterisk,
2785 .op = .{ .PtrType = .{} },
2786 .rhs = undefined, // set by caller2893 .rhs = undefined, // set by caller
2787 };2894 };
27882895
2789 // Special case for **, which is its own token2896 // Special case for **, which is its own token
2790 const child = try p.arena.allocator.create(Node.PrefixOp);2897 const child = try p.arena.allocator.create(Node.PtrType);
2791 child.* = .{2898 child.* = .{
2792 .op_token = double_asterisk,2899 .op_token = double_asterisk,
2793 .op = .{ .PtrType = .{} },
2794 .rhs = undefined, // set by caller2900 .rhs = undefined, // set by caller
2795 };2901 };
2796 node.rhs = &child.base;2902 node.rhs = &child.base;
...@@ -2809,10 +2915,9 @@ const Parser = struct {...@@ -2809,10 +2915,9 @@ const Parser = struct {
2809 p.putBackToken(ident);2915 p.putBackToken(ident);
2810 } else {2916 } else {
2811 _ = try p.expectToken(.RBracket);2917 _ = try p.expectToken(.RBracket);
2812 const node = try p.arena.allocator.create(Node.PrefixOp);2918 const node = try p.arena.allocator.create(Node.PtrType);
2813 node.* = .{2919 node.* = .{
2814 .op_token = lbracket,2920 .op_token = lbracket,
2815 .op = .{ .PtrType = .{} },
2816 .rhs = undefined, // set by caller2921 .rhs = undefined, // set by caller
2817 };2922 };
2818 return &node.base;2923 return &node.base;
...@@ -2825,11 +2930,11 @@ const Parser = struct {...@@ -2825,11 +2930,11 @@ const Parser = struct {
2825 else2930 else
2826 null;2931 null;
2827 _ = try p.expectToken(.RBracket);2932 _ = try p.expectToken(.RBracket);
2828 const node = try p.arena.allocator.create(Node.PrefixOp);2933 const node = try p.arena.allocator.create(Node.PtrType);
2829 node.* = .{2934 node.* = .{
2830 .op_token = lbracket,2935 .op_token = lbracket,
2831 .op = .{ .PtrType = .{ .sentinel = sentinel } },
2832 .rhs = undefined, // set by caller2936 .rhs = undefined, // set by caller
2937 .ptr_info = .{ .sentinel = sentinel },
2833 };2938 };
2834 return &node.base;2939 return &node.base;
2835 }2940 }
...@@ -2956,7 +3061,7 @@ const Parser = struct {...@@ -2956,7 +3061,7 @@ const Parser = struct {
29563061
2957 const NodeParseFn = fn (p: *Parser) Error!?*Node;3062 const NodeParseFn = fn (p: *Parser) Error!?*Node;
29583063
2959 fn ListParseFn(comptime E: type, comptime nodeParseFn: var) ParseFn([]E) {3064 fn ListParseFn(comptime E: type, comptime nodeParseFn: anytype) ParseFn([]E) {
2960 return struct {3065 return struct {
2961 pub fn parse(p: *Parser) ![]E {3066 pub fn parse(p: *Parser) ![]E {
2962 var list = std.ArrayList(E).init(p.gpa);3067 var list = std.ArrayList(E).init(p.gpa);
...@@ -2983,7 +3088,7 @@ const Parser = struct {...@@ -2983,7 +3088,7 @@ const Parser = struct {
2983 }.parse;3088 }.parse;
2984 }3089 }
29853090
2986 fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.InfixOp.Op) NodeParseFn {3091 fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.Tag) NodeParseFn {
2987 return struct {3092 return struct {
2988 pub fn parse(p: *Parser) Error!?*Node {3093 pub fn parse(p: *Parser) Error!?*Node {
2989 const op_token = if (token == .Keyword_and) switch (p.token_ids[p.tok_i]) {3094 const op_token = if (token == .Keyword_and) switch (p.token_ids[p.tok_i]) {
...@@ -2997,11 +3102,11 @@ const Parser = struct {...@@ -2997,11 +3102,11 @@ const Parser = struct {
2997 else => return null,3102 else => return null,
2998 } else p.eatToken(token) orelse return null;3103 } else p.eatToken(token) orelse return null;
29993104
3000 const node = try p.arena.allocator.create(Node.InfixOp);3105 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
3001 node.* = .{3106 node.* = .{
3107 .base = .{ .tag = op },
3002 .op_token = op_token,3108 .op_token = op_token,
3003 .lhs = undefined, // set by caller3109 .lhs = undefined, // set by caller
3004 .op = op,
3005 .rhs = undefined, // set by caller3110 .rhs = undefined, // set by caller
3006 };3111 };
3007 return &node.base;3112 return &node.base;
...@@ -3058,9 +3163,10 @@ const Parser = struct {...@@ -3058,9 +3163,10 @@ const Parser = struct {
3058 return &node.base;3163 return &node.base;
3059 }3164 }
30603165
3061 fn parseVarType(p: *Parser) !?*Node {3166 fn parseAnyType(p: *Parser) !?*Node {
3062 const token = p.eatToken(.Keyword_var) orelse return null;3167 const token = p.eatToken(.Keyword_anytype) orelse
3063 const node = try p.arena.allocator.create(Node.VarType);3168 p.eatToken(.Keyword_var) orelse return null; // TODO remove in next release cycle
3169 const node = try p.arena.allocator.create(Node.AnyType);
3064 node.* = .{3170 node.* = .{
3065 .token = token,3171 .token = token,
3066 };3172 };
...@@ -3070,7 +3176,6 @@ const Parser = struct {...@@ -3070,7 +3176,6 @@ const Parser = struct {
3070 fn createLiteral(p: *Parser, comptime T: type, token: TokenIndex) !*Node {3176 fn createLiteral(p: *Parser, comptime T: type, token: TokenIndex) !*Node {
3071 const result = try p.arena.allocator.create(T);3177 const result = try p.arena.allocator.create(T);
3072 result.* = T{3178 result.* = T{
3073 .base = Node{ .id = Node.typeToId(T) },
3074 .token = token,3179 .token = token,
3075 };3180 };
3076 return &result.base;3181 return &result.base;
...@@ -3146,30 +3251,15 @@ const Parser = struct {...@@ -3146,30 +3251,15 @@ const Parser = struct {
31463251
3147 fn parseTry(p: *Parser) !?*Node {3252 fn parseTry(p: *Parser) !?*Node {
3148 const token = p.eatToken(.Keyword_try) orelse return null;3253 const token = p.eatToken(.Keyword_try) orelse return null;
3149 const node = try p.arena.allocator.create(Node.PrefixOp);3254 const node = try p.arena.allocator.create(Node.SimplePrefixOp);
3150 node.* = .{3255 node.* = .{
3256 .base = .{ .tag = .Try },
3151 .op_token = token,3257 .op_token = token,
3152 .op = .Try,
3153 .rhs = undefined, // set by caller3258 .rhs = undefined, // set by caller
3154 };3259 };
3155 return &node.base;3260 return &node.base;
3156 }3261 }
31573262
3158 fn parseUse(p: *Parser) !?*Node {
3159 const token = p.eatToken(.Keyword_usingnamespace) orelse return null;
3160 const node = try p.arena.allocator.create(Node.Use);
3161 node.* = .{
3162 .doc_comments = null,
3163 .visib_token = null,
3164 .use_token = token,
3165 .expr = try p.expectNode(parseExpr, .{
3166 .ExpectedExpr = .{ .token = p.tok_i },
3167 }),
3168 .semicolon_token = try p.expectToken(.Semicolon),
3169 };
3170 return &node.base;
3171 }
3172
3173 /// IfPrefix Body (KEYWORD_else Payload? Body)?3263 /// IfPrefix Body (KEYWORD_else Payload? Body)?
3174 fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !?*Node {3264 fn parseIf(p: *Parser, bodyParseFn: NodeParseFn) !?*Node {
3175 const node = (try p.parseIfPrefix()) orelse return null;3265 const node = (try p.parseIfPrefix()) orelse return null;
...@@ -3223,20 +3313,53 @@ const Parser = struct {...@@ -3223,20 +3313,53 @@ const Parser = struct {
3223 }3313 }
32243314
3225 /// Op* Child3315 /// Op* Child
3226 fn parsePrefixOpExpr(p: *Parser, opParseFn: NodeParseFn, childParseFn: NodeParseFn) Error!?*Node {3316 fn parsePrefixOpExpr(p: *Parser, comptime opParseFn: NodeParseFn, comptime childParseFn: NodeParseFn) Error!?*Node {
3227 if (try opParseFn(p)) |first_op| {3317 if (try opParseFn(p)) |first_op| {
3228 var rightmost_op = first_op;3318 var rightmost_op = first_op;
3229 while (true) {3319 while (true) {
3230 switch (rightmost_op.id) {3320 switch (rightmost_op.tag) {
3231 .PrefixOp => {3321 .AddressOf,
3232 var prefix_op = rightmost_op.cast(Node.PrefixOp).?;3322 .Await,
3323 .BitNot,
3324 .BoolNot,
3325 .OptionalType,
3326 .Negation,
3327 .NegationWrap,
3328 .Resume,
3329 .Try,
3330 => {
3331 if (try opParseFn(p)) |rhs| {
3332 rightmost_op.cast(Node.SimplePrefixOp).?.rhs = rhs;
3333 rightmost_op = rhs;
3334 } else break;
3335 },
3336 .ArrayType => {
3337 if (try opParseFn(p)) |rhs| {
3338 rightmost_op.cast(Node.ArrayType).?.rhs = rhs;
3339 rightmost_op = rhs;
3340 } else break;
3341 },
3342 .ArrayTypeSentinel => {
3343 if (try opParseFn(p)) |rhs| {
3344 rightmost_op.cast(Node.ArrayTypeSentinel).?.rhs = rhs;
3345 rightmost_op = rhs;
3346 } else break;
3347 },
3348 .SliceType => {
3349 if (try opParseFn(p)) |rhs| {
3350 rightmost_op.cast(Node.SliceType).?.rhs = rhs;
3351 rightmost_op = rhs;
3352 } else break;
3353 },
3354 .PtrType => {
3355 var ptr_type = rightmost_op.cast(Node.PtrType).?;
3233 // If the token encountered was **, there will be two nodes3356 // If the token encountered was **, there will be two nodes
3234 if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk) {3357 if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) {
3235 rightmost_op = prefix_op.rhs;3358 rightmost_op = ptr_type.rhs;
3236 prefix_op = rightmost_op.cast(Node.PrefixOp).?;3359 ptr_type = rightmost_op.cast(Node.PtrType).?;
3237 }3360 }
3238 if (try opParseFn(p)) |rhs| {3361 if (try opParseFn(p)) |rhs| {
3239 prefix_op.rhs = rhs;3362 ptr_type.rhs = rhs;
3240 rightmost_op = rhs;3363 rightmost_op = rhs;
3241 } else break;3364 } else break;
3242 },3365 },
...@@ -3252,9 +3375,42 @@ const Parser = struct {...@@ -3252,9 +3375,42 @@ const Parser = struct {
3252 }3375 }
32533376
3254 // If any prefix op existed, a child node on the RHS is required3377 // If any prefix op existed, a child node on the RHS is required
3255 switch (rightmost_op.id) {3378 switch (rightmost_op.tag) {
3256 .PrefixOp => {3379 .AddressOf,
3257 const prefix_op = rightmost_op.cast(Node.PrefixOp).?;3380 .Await,
3381 .BitNot,
3382 .BoolNot,
3383 .OptionalType,
3384 .Negation,
3385 .NegationWrap,
3386 .Resume,
3387 .Try,
3388 => {
3389 const prefix_op = rightmost_op.cast(Node.SimplePrefixOp).?;
3390 prefix_op.rhs = try p.expectNode(childParseFn, .{
3391 .InvalidToken = .{ .token = p.tok_i },
3392 });
3393 },
3394 .ArrayType => {
3395 const prefix_op = rightmost_op.cast(Node.ArrayType).?;
3396 prefix_op.rhs = try p.expectNode(childParseFn, .{
3397 .InvalidToken = .{ .token = p.tok_i },
3398 });
3399 },
3400 .ArrayTypeSentinel => {
3401 const prefix_op = rightmost_op.cast(Node.ArrayTypeSentinel).?;
3402 prefix_op.rhs = try p.expectNode(childParseFn, .{
3403 .InvalidToken = .{ .token = p.tok_i },
3404 });
3405 },
3406 .PtrType => {
3407 const prefix_op = rightmost_op.cast(Node.PtrType).?;
3408 prefix_op.rhs = try p.expectNode(childParseFn, .{
3409 .InvalidToken = .{ .token = p.tok_i },
3410 });
3411 },
3412 .SliceType => {
3413 const prefix_op = rightmost_op.cast(Node.SliceType).?;
3258 prefix_op.rhs = try p.expectNode(childParseFn, .{3414 prefix_op.rhs = try p.expectNode(childParseFn, .{
3259 .InvalidToken = .{ .token = p.tok_i },3415 .InvalidToken = .{ .token = p.tok_i },
3260 });3416 });
...@@ -3295,9 +3451,13 @@ const Parser = struct {...@@ -3295,9 +3451,13 @@ const Parser = struct {
3295 const left = res;3451 const left = res;
3296 res = node;3452 res = node;
32973453
3298 const op = node.cast(Node.InfixOp).?;3454 if (node.castTag(.Catch)) |op| {
3299 op.*.lhs = left;3455 op.lhs = left;
3300 op.*.rhs = right;3456 op.rhs = right;
3457 } else if (node.cast(Node.SimpleInfixOp)) |op| {
3458 op.lhs = left;
3459 op.rhs = right;
3460 }
33013461
3302 switch (chain) {3462 switch (chain) {
3303 .Once => break,3463 .Once => break,
...@@ -3308,12 +3468,12 @@ const Parser = struct {...@@ -3308,12 +3468,12 @@ const Parser = struct {
3308 return res;3468 return res;
3309 }3469 }
33103470
3311 fn createInfixOp(p: *Parser, index: TokenIndex, op: Node.InfixOp.Op) !*Node {3471 fn createInfixOp(p: *Parser, op_token: TokenIndex, tag: Node.Tag) !*Node {
3312 const node = try p.arena.allocator.create(Node.InfixOp);3472 const node = try p.arena.allocator.create(Node.SimpleInfixOp);
3313 node.* = .{3473 node.* = .{
3314 .op_token = index,3474 .base = Node{ .tag = tag },
3475 .op_token = op_token,
3315 .lhs = undefined, // set by caller3476 .lhs = undefined, // set by caller
3316 .op = op,
3317 .rhs = undefined, // set by caller3477 .rhs = undefined, // set by caller
3318 };3478 };
3319 return &node.base;3479 return &node.base;
lib/std/zig/parser_test.zig+41-20
...@@ -1,4 +1,32 @@...@@ -1,4 +1,32 @@
1const builtin = @import("builtin");1test "zig fmt: convert var to anytype" {
2 // TODO remove in next release cycle
3 try testTransform(
4 \\pub fn main(
5 \\ a: var,
6 \\ bar: var,
7 \\) void {}
8 ,
9 \\pub fn main(
10 \\ a: anytype,
11 \\ bar: anytype,
12 \\) void {}
13 \\
14 );
15}
16
17test "zig fmt: noasync to nosuspend" {
18 // TODO: remove this
19 try testTransform(
20 \\pub fn main() void {
21 \\ noasync call();
22 \\}
23 ,
24 \\pub fn main() void {
25 \\ nosuspend call();
26 \\}
27 \\
28 );
29}
230
3test "recovery: top level" {31test "recovery: top level" {
4 try testError(32 try testError(
...@@ -422,10 +450,10 @@ test "zig fmt: asm expression with comptime content" {...@@ -422,10 +450,10 @@ test "zig fmt: asm expression with comptime content" {
422 );450 );
423}451}
424452
425test "zig fmt: var struct field" {453test "zig fmt: anytype struct field" {
426 try testCanonical(454 try testCanonical(
427 \\pub const Pointer = struct {455 \\pub const Pointer = struct {
428 \\ sentinel: var,456 \\ sentinel: anytype,
429 \\};457 \\};
430 \\458 \\
431 );459 );
...@@ -1932,7 +1960,7 @@ test "zig fmt: preserve spacing" {...@@ -1932,7 +1960,7 @@ test "zig fmt: preserve spacing" {
1932test "zig fmt: return types" {1960test "zig fmt: return types" {
1933 try testCanonical(1961 try testCanonical(
1934 \\pub fn main() !void {}1962 \\pub fn main() !void {}
1935 \\pub fn main() var {}1963 \\pub fn main() anytype {}
1936 \\pub fn main() i32 {}1964 \\pub fn main() i32 {}
1937 \\1965 \\
1938 );1966 );
...@@ -2140,9 +2168,9 @@ test "zig fmt: call expression" {...@@ -2140,9 +2168,9 @@ test "zig fmt: call expression" {
2140 );2168 );
2141}2169}
21422170
2143test "zig fmt: var type" {2171test "zig fmt: anytype type" {
2144 try testCanonical(2172 try testCanonical(
2145 \\fn print(args: var) var {}2173 \\fn print(args: anytype) anytype {}
2146 \\2174 \\
2147 );2175 );
2148}2176}
...@@ -3146,20 +3174,6 @@ test "zig fmt: hexadeciaml float literals with underscore separators" {...@@ -3146,20 +3174,6 @@ test "zig fmt: hexadeciaml float literals with underscore separators" {
3146 );3174 );
3147}3175}
31483176
3149test "zig fmt: noasync to nosuspend" {
3150 // TODO: remove this
3151 try testTransform(
3152 \\pub fn main() void {
3153 \\ noasync call();
3154 \\}
3155 ,
3156 \\pub fn main() void {
3157 \\ nosuspend call();
3158 \\}
3159 \\
3160 );
3161}
3162
3163test "zig fmt: convert async fn into callconv(.Async)" {3177test "zig fmt: convert async fn into callconv(.Async)" {
3164 try testTransform(3178 try testTransform(
3165 \\async fn foo() void {}3179 \\async fn foo() void {}
...@@ -3180,6 +3194,13 @@ test "zig fmt: convert extern fn proto into callconv(.C)" {...@@ -3180,6 +3194,13 @@ test "zig fmt: convert extern fn proto into callconv(.C)" {
3180 );3194 );
3181}3195}
31823196
3197test "zig fmt: C var args" {
3198 try testCanonical(
3199 \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
3200 \\
3201 );
3202}
3203
3183const std = @import("std");3204const std = @import("std");
3184const mem = std.mem;3205const mem = std.mem;
3185const warn = std.debug.warn;3206const warn = std.debug.warn;
lib/std/zig/render.zig+396-234
...@@ -12,7 +12,7 @@ pub const Error = error{...@@ -12,7 +12,7 @@ pub const Error = error{
12};12};
1313
14/// Returns whether anything changed14/// Returns whether anything changed
15pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool {15pub fn render(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool {
16 // cannot render an invalid tree16 // cannot render an invalid tree
17 std.debug.assert(tree.errors.len == 0);17 std.debug.assert(tree.errors.len == 0);
1818
...@@ -64,7 +64,7 @@ pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(...@@ -64,7 +64,7 @@ pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(
6464
65fn renderRoot(65fn renderRoot(
66 allocator: *mem.Allocator,66 allocator: *mem.Allocator,
67 stream: var,67 stream: anytype,
68 tree: *ast.Tree,68 tree: *ast.Tree,
69) (@TypeOf(stream).Error || Error)!void {69) (@TypeOf(stream).Error || Error)!void {
70 // render all the line comments at the beginning of the file70 // render all the line comments at the beginning of the file
...@@ -191,13 +191,13 @@ fn renderRoot(...@@ -191,13 +191,13 @@ fn renderRoot(
191 }191 }
192}192}
193193
194fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @TypeOf(stream).Error!void {194fn renderExtraNewline(tree: *ast.Tree, stream: anytype, start_col: *usize, node: *ast.Node) @TypeOf(stream).Error!void {
195 return renderExtraNewlineToken(tree, stream, start_col, node.firstToken());195 return renderExtraNewlineToken(tree, stream, start_col, node.firstToken());
196}196}
197197
198fn renderExtraNewlineToken(198fn renderExtraNewlineToken(
199 tree: *ast.Tree,199 tree: *ast.Tree,
200 stream: var,200 stream: anytype,
201 start_col: *usize,201 start_col: *usize,
202 first_token: ast.TokenIndex,202 first_token: ast.TokenIndex,
203) @TypeOf(stream).Error!void {203) @TypeOf(stream).Error!void {
...@@ -218,18 +218,18 @@ fn renderExtraNewlineToken(...@@ -218,18 +218,18 @@ fn renderExtraNewlineToken(
218 }218 }
219}219}
220220
221fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Error || Error)!void {221fn renderTopLevelDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Error || Error)!void {
222 try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Newline);222 try renderContainerDecl(allocator, stream, tree, indent, start_col, decl, .Newline);
223}223}
224224
225fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Error || Error)!void {225fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Error || Error)!void {
226 switch (decl.id) {226 switch (decl.tag) {
227 .FnProto => {227 .FnProto => {
228 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);228 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
229229
230 try renderDocComments(tree, stream, fn_proto, indent, start_col);230 try renderDocComments(tree, stream, fn_proto, fn_proto.getTrailer("doc_comments"), indent, start_col);
231231
232 if (fn_proto.body_node) |body_node| {232 if (fn_proto.getTrailer("body_node")) |body_node| {
233 try renderExpression(allocator, stream, tree, indent, start_col, decl, .Space);233 try renderExpression(allocator, stream, tree, indent, start_col, decl, .Space);
234 try renderExpression(allocator, stream, tree, indent, start_col, body_node, space);234 try renderExpression(allocator, stream, tree, indent, start_col, body_node, space);
235 } else {235 } else {
...@@ -252,14 +252,14 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree,...@@ -252,14 +252,14 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree,
252 .VarDecl => {252 .VarDecl => {
253 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);253 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
254254
255 try renderDocComments(tree, stream, var_decl, indent, start_col);255 try renderDocComments(tree, stream, var_decl, var_decl.getTrailer("doc_comments"), indent, start_col);
256 try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl);256 try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl);
257 },257 },
258258
259 .TestDecl => {259 .TestDecl => {
260 const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);260 const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
261261
262 try renderDocComments(tree, stream, test_decl, indent, start_col);262 try renderDocComments(tree, stream, test_decl, test_decl.doc_comments, indent, start_col);
263 try renderToken(tree, stream, test_decl.test_token, indent, start_col, .Space);263 try renderToken(tree, stream, test_decl.test_token, indent, start_col, .Space);
264 try renderExpression(allocator, stream, tree, indent, start_col, test_decl.name, .Space);264 try renderExpression(allocator, stream, tree, indent, start_col, test_decl.name, .Space);
265 try renderExpression(allocator, stream, tree, indent, start_col, test_decl.body_node, space);265 try renderExpression(allocator, stream, tree, indent, start_col, test_decl.body_node, space);
...@@ -268,7 +268,7 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree,...@@ -268,7 +268,7 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree,
268 .ContainerField => {268 .ContainerField => {
269 const field = @fieldParentPtr(ast.Node.ContainerField, "base", decl);269 const field = @fieldParentPtr(ast.Node.ContainerField, "base", decl);
270270
271 try renderDocComments(tree, stream, field, indent, start_col);271 try renderDocComments(tree, stream, field, field.doc_comments, indent, start_col);
272 if (field.comptime_token) |t| {272 if (field.comptime_token) |t| {
273 try renderToken(tree, stream, t, indent, start_col, .Space); // comptime273 try renderToken(tree, stream, t, indent, start_col, .Space); // comptime
274 }274 }
...@@ -358,14 +358,14 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree,...@@ -358,14 +358,14 @@ fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree,
358358
359fn renderExpression(359fn renderExpression(
360 allocator: *mem.Allocator,360 allocator: *mem.Allocator,
361 stream: var,361 stream: anytype,
362 tree: *ast.Tree,362 tree: *ast.Tree,
363 indent: usize,363 indent: usize,
364 start_col: *usize,364 start_col: *usize,
365 base: *ast.Node,365 base: *ast.Node,
366 space: Space,366 space: Space,
367) (@TypeOf(stream).Error || Error)!void {367) (@TypeOf(stream).Error || Error)!void {
368 switch (base.id) {368 switch (base.tag) {
369 .Identifier => {369 .Identifier => {
370 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);370 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
371 return renderToken(tree, stream, identifier.token, indent, start_col, space);371 return renderToken(tree, stream, identifier.token, indent, start_col, space);
...@@ -436,13 +436,10 @@ fn renderExpression(...@@ -436,13 +436,10 @@ fn renderExpression(
436 }436 }
437 },437 },
438438
439 .InfixOp => {439 .Catch => {
440 const infix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base);440 const infix_op_node = @fieldParentPtr(ast.Node.Catch, "base", base);
441441
442 const op_space = switch (infix_op_node.op) {442 const op_space = Space.Space;
443 ast.Node.InfixOp.Op.Period, ast.Node.InfixOp.Op.ErrorUnion, ast.Node.InfixOp.Op.Range => Space.None,
444 else => Space.Space,
445 };
446 try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space);443 try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space);
447444
448 const after_op_space = blk: {445 const after_op_space = blk: {
...@@ -458,182 +455,247 @@ fn renderExpression(...@@ -458,182 +455,247 @@ fn renderExpression(
458 start_col.* = indent + indent_delta;455 start_col.* = indent + indent_delta;
459 }456 }
460457
461 switch (infix_op_node.op) {458 if (infix_op_node.payload) |payload| {
462 ast.Node.InfixOp.Op.Catch => |maybe_payload| if (maybe_payload) |payload| {459 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
463 try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
464 },
465 else => {},
466 }460 }
467461
468 return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);462 return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);
469 },463 },
470464
471 .PrefixOp => {465 .Add,
472 const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);466 .AddWrap,
473467 .ArrayCat,
474 switch (prefix_op_node.op) {468 .ArrayMult,
475 .PtrType => |ptr_info| {469 .Assign,
476 const op_tok_id = tree.token_ids[prefix_op_node.op_token];470 .AssignBitAnd,
477 switch (op_tok_id) {471 .AssignBitOr,
478 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),472 .AssignBitShiftLeft,
479 .LBracket => if (tree.token_ids[prefix_op_node.op_token + 2] == .Identifier)473 .AssignBitShiftRight,
480 try stream.writeAll("[*c")474 .AssignBitXor,
481 else475 .AssignDiv,
482 try stream.writeAll("[*"),476 .AssignSub,
483 else => unreachable,477 .AssignSubWrap,
484 }478 .AssignMod,
485 if (ptr_info.sentinel) |sentinel| {479 .AssignAdd,
486 const colon_token = tree.prevToken(sentinel.firstToken());480 .AssignAddWrap,
487 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :481 .AssignMul,
488 const sentinel_space = switch (op_tok_id) {482 .AssignMulWrap,
489 .LBracket => Space.None,483 .BangEqual,
490 else => Space.Space,484 .BitAnd,
491 };485 .BitOr,
492 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space);486 .BitShiftLeft,
493 }487 .BitShiftRight,
494 switch (op_tok_id) {488 .BitXor,
495 .Asterisk, .AsteriskAsterisk => {},489 .BoolAnd,
496 .LBracket => try stream.writeByte(']'),490 .BoolOr,
497 else => unreachable,491 .Div,
498 }492 .EqualEqual,
499 if (ptr_info.allowzero_token) |allowzero_token| {493 .ErrorUnion,
500 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero494 .GreaterOrEqual,
501 }495 .GreaterThan,
502 if (ptr_info.align_info) |align_info| {496 .LessOrEqual,
503 const lparen_token = tree.prevToken(align_info.node.firstToken());497 .LessThan,
504 const align_token = tree.prevToken(lparen_token);498 .MergeErrorSets,
499 .Mod,
500 .Mul,
501 .MulWrap,
502 .Period,
503 .Range,
504 .Sub,
505 .SubWrap,
506 .UnwrapOptional,
507 => {
508 const infix_op_node = @fieldParentPtr(ast.Node.SimpleInfixOp, "base", base);
509
510 const op_space = switch (base.tag) {
511 .Period, .ErrorUnion, .Range => Space.None,
512 else => Space.Space,
513 };
514 try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space);
505515
506 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align516 const after_op_space = blk: {
507 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (517 const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));
518 break :blk if (loc.line == 0) op_space else Space.Newline;
519 };
508520
509 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);521 try renderToken(tree, stream, infix_op_node.op_token, indent, start_col, after_op_space);
522 if (after_op_space == Space.Newline and
523 tree.token_ids[tree.nextToken(infix_op_node.op_token)] != .MultilineStringLiteralLine)
524 {
525 try stream.writeByteNTimes(' ', indent + indent_delta);
526 start_col.* = indent + indent_delta;
527 }
510528
511 if (align_info.bit_range) |bit_range| {529 return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);
512 const colon1 = tree.prevToken(bit_range.start.firstToken());530 },
513 const colon2 = tree.prevToken(bit_range.end.firstToken());
514531
515 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :532 .BitNot,
516 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);533 .BoolNot,
517 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :534 .Negation,
518 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);535 .NegationWrap,
536 .OptionalType,
537 .AddressOf,
538 => {
539 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
540 try renderToken(tree, stream, casted_node.op_token, indent, start_col, Space.None);
541 return renderExpression(allocator, stream, tree, indent, start_col, casted_node.rhs, space);
542 },
519543
520 const rparen_token = tree.nextToken(bit_range.end.lastToken());544 .Try,
521 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )545 .Resume,
522 } else {546 .Await,
523 const rparen_token = tree.nextToken(align_info.node.lastToken());547 => {
524 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )548 const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
525 }549 try renderToken(tree, stream, casted_node.op_token, indent, start_col, Space.Space);
526 }550 return renderExpression(allocator, stream, tree, indent, start_col, casted_node.rhs, space);
527 if (ptr_info.const_token) |const_token| {551 },
528 try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const
529 }
530 if (ptr_info.volatile_token) |volatile_token| {
531 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile
532 }
533 },
534552
535 .SliceType => |ptr_info| {553 .ArrayType => {
536 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [554 const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base);
537 if (ptr_info.sentinel) |sentinel| {555 return renderArrayType(
538 const colon_token = tree.prevToken(sentinel.firstToken());556 allocator,
539 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :557 stream,
540 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);558 tree,
541 try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ]559 indent,
542 } else {560 start_col,
543 try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ]561 array_type.op_token,
544 }562 array_type.rhs,
563 array_type.len_expr,
564 null,
565 space,
566 );
567 },
568 .ArrayTypeSentinel => {
569 const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base);
570 return renderArrayType(
571 allocator,
572 stream,
573 tree,
574 indent,
575 start_col,
576 array_type.op_token,
577 array_type.rhs,
578 array_type.len_expr,
579 array_type.sentinel,
580 space,
581 );
582 },
545583
546 if (ptr_info.allowzero_token) |allowzero_token| {584 .PtrType => {
547 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero585 const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base);
548 }586 const op_tok_id = tree.token_ids[ptr_type.op_token];
549 if (ptr_info.align_info) |align_info| {587 switch (op_tok_id) {
550 const lparen_token = tree.prevToken(align_info.node.firstToken());588 .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'),
551 const align_token = tree.prevToken(lparen_token);589 .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier)
590 try stream.writeAll("[*c")
591 else
592 try stream.writeAll("[*"),
593 else => unreachable,
594 }
595 if (ptr_type.ptr_info.sentinel) |sentinel| {
596 const colon_token = tree.prevToken(sentinel.firstToken());
597 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
598 const sentinel_space = switch (op_tok_id) {
599 .LBracket => Space.None,
600 else => Space.Space,
601 };
602 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space);
603 }
604 switch (op_tok_id) {
605 .Asterisk, .AsteriskAsterisk => {},
606 .LBracket => try stream.writeByte(']'),
607 else => unreachable,
608 }
609 if (ptr_type.ptr_info.allowzero_token) |allowzero_token| {
610 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
611 }
612 if (ptr_type.ptr_info.align_info) |align_info| {
613 const lparen_token = tree.prevToken(align_info.node.firstToken());
614 const align_token = tree.prevToken(lparen_token);
552615
553 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align616 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
554 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (617 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
555618
556 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);619 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
557620
558 if (align_info.bit_range) |bit_range| {621 if (align_info.bit_range) |bit_range| {
559 const colon1 = tree.prevToken(bit_range.start.firstToken());622 const colon1 = tree.prevToken(bit_range.start.firstToken());
560 const colon2 = tree.prevToken(bit_range.end.firstToken());623 const colon2 = tree.prevToken(bit_range.end.firstToken());
561624
562 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :625 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
563 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);626 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
564 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :627 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
565 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);628 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
566629
567 const rparen_token = tree.nextToken(bit_range.end.lastToken());630 const rparen_token = tree.nextToken(bit_range.end.lastToken());
568 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )631 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
569 } else {632 } else {
570 const rparen_token = tree.nextToken(align_info.node.lastToken());633 const rparen_token = tree.nextToken(align_info.node.lastToken());
571 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )634 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
572 }635 }
573 }636 }
574 if (ptr_info.const_token) |const_token| {637 if (ptr_type.ptr_info.const_token) |const_token| {
575 try renderToken(tree, stream, const_token, indent, start_col, Space.Space);638 try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const
576 }639 }
577 if (ptr_info.volatile_token) |volatile_token| {640 if (ptr_type.ptr_info.volatile_token) |volatile_token| {
578 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space);641 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile
579 }642 }
580 },643 return renderExpression(allocator, stream, tree, indent, start_col, ptr_type.rhs, space);
644 },
581645
582 .ArrayType => |array_info| {646 .SliceType => {
583 const lbracket = prefix_op_node.op_token;647 const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base);
584 const rbracket = tree.nextToken(if (array_info.sentinel) |sentinel|648 try renderToken(tree, stream, slice_type.op_token, indent, start_col, Space.None); // [
585 sentinel.lastToken()649 if (slice_type.ptr_info.sentinel) |sentinel| {
586 else650 const colon_token = tree.prevToken(sentinel.firstToken());
587 array_info.len_expr.lastToken());651 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
652 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
653 try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ]
654 } else {
655 try renderToken(tree, stream, tree.nextToken(slice_type.op_token), indent, start_col, Space.None); // ]
656 }
588657
589 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [658 if (slice_type.ptr_info.allowzero_token) |allowzero_token| {
659 try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero
660 }
661 if (slice_type.ptr_info.align_info) |align_info| {
662 const lparen_token = tree.prevToken(align_info.node.firstToken());
663 const align_token = tree.prevToken(lparen_token);
590664
591 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;665 try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align
592 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;666 try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // (
593 const new_indent = if (ends_with_comment) indent + indent_delta else indent;
594 const new_space = if (ends_with_comment) Space.Newline else Space.None;
595 try renderExpression(allocator, stream, tree, new_indent, start_col, array_info.len_expr, new_space);
596 if (starts_with_comment) {
597 try stream.writeByte('\n');
598 }
599 if (ends_with_comment or starts_with_comment) {
600 try stream.writeByteNTimes(' ', indent);
601 }
602 if (array_info.sentinel) |sentinel| {
603 const colon_token = tree.prevToken(sentinel.firstToken());
604 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
605 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
606 }
607 try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ]
608 },
609 .BitNot,
610 .BoolNot,
611 .Negation,
612 .NegationWrap,
613 .OptionalType,
614 .AddressOf,
615 => {
616 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None);
617 },
618667
619 .Try,668 try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None);
620 .Resume,
621 => {
622 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);
623 },
624669
625 .Await => |await_info| {670 if (align_info.bit_range) |bit_range| {
626 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);671 const colon1 = tree.prevToken(bit_range.start.firstToken());
627 },672 const colon2 = tree.prevToken(bit_range.end.firstToken());
628 }673
674 try renderToken(tree, stream, colon1, indent, start_col, Space.None); // :
675 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None);
676 try renderToken(tree, stream, colon2, indent, start_col, Space.None); // :
677 try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None);
629678
630 return renderExpression(allocator, stream, tree, indent, start_col, prefix_op_node.rhs, space);679 const rparen_token = tree.nextToken(bit_range.end.lastToken());
680 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
681 } else {
682 const rparen_token = tree.nextToken(align_info.node.lastToken());
683 try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // )
684 }
685 }
686 if (slice_type.ptr_info.const_token) |const_token| {
687 try renderToken(tree, stream, const_token, indent, start_col, Space.Space);
688 }
689 if (slice_type.ptr_info.volatile_token) |volatile_token| {
690 try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space);
691 }
692 return renderExpression(allocator, stream, tree, indent, start_col, slice_type.rhs, space);
631 },693 },
632694
633 .ArrayInitializer, .ArrayInitializerDot => {695 .ArrayInitializer, .ArrayInitializerDot => {
634 var rtoken: ast.TokenIndex = undefined;696 var rtoken: ast.TokenIndex = undefined;
635 var exprs: []*ast.Node = undefined;697 var exprs: []*ast.Node = undefined;
636 const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.id) {698 const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) {
637 .ArrayInitializerDot => blk: {699 .ArrayInitializerDot => blk: {
638 const casted = @fieldParentPtr(ast.Node.ArrayInitializerDot, "base", base);700 const casted = @fieldParentPtr(ast.Node.ArrayInitializerDot, "base", base);
639 rtoken = casted.rtoken;701 rtoken = casted.rtoken;
...@@ -767,14 +829,14 @@ fn renderExpression(...@@ -767,14 +829,14 @@ fn renderExpression(
767 }829 }
768830
769 try renderExtraNewline(tree, stream, start_col, next_expr);831 try renderExtraNewline(tree, stream, start_col, next_expr);
770 if (next_expr.id != .MultilineStringLiteral) {832 if (next_expr.tag != .MultilineStringLiteral) {
771 try stream.writeByteNTimes(' ', new_indent);833 try stream.writeByteNTimes(' ', new_indent);
772 }834 }
773 } else {835 } else {
774 try renderExpression(allocator, stream, tree, new_indent, start_col, expr, Space.Comma); // ,836 try renderExpression(allocator, stream, tree, new_indent, start_col, expr, Space.Comma); // ,
775 }837 }
776 }838 }
777 if (exprs[exprs.len - 1].id != .MultilineStringLiteral) {839 if (exprs[exprs.len - 1].tag != .MultilineStringLiteral) {
778 try stream.writeByteNTimes(' ', indent);840 try stream.writeByteNTimes(' ', indent);
779 }841 }
780 return renderToken(tree, stream, rtoken, indent, start_col, space);842 return renderToken(tree, stream, rtoken, indent, start_col, space);
...@@ -797,7 +859,7 @@ fn renderExpression(...@@ -797,7 +859,7 @@ fn renderExpression(
797 .StructInitializer, .StructInitializerDot => {859 .StructInitializer, .StructInitializerDot => {
798 var rtoken: ast.TokenIndex = undefined;860 var rtoken: ast.TokenIndex = undefined;
799 var field_inits: []*ast.Node = undefined;861 var field_inits: []*ast.Node = undefined;
800 const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.id) {862 const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) {
801 .StructInitializerDot => blk: {863 .StructInitializerDot => blk: {
802 const casted = @fieldParentPtr(ast.Node.StructInitializerDot, "base", base);864 const casted = @fieldParentPtr(ast.Node.StructInitializerDot, "base", base);
803 rtoken = casted.rtoken;865 rtoken = casted.rtoken;
...@@ -851,7 +913,7 @@ fn renderExpression(...@@ -851,7 +913,7 @@ fn renderExpression(
851 if (field_inits.len == 1) blk: {913 if (field_inits.len == 1) blk: {
852 const field_init = field_inits[0].cast(ast.Node.FieldInitializer).?;914 const field_init = field_inits[0].cast(ast.Node.FieldInitializer).?;
853915
854 switch (field_init.expr.id) {916 switch (field_init.expr.tag) {
855 .StructInitializer,917 .StructInitializer,
856 .StructInitializerDot,918 .StructInitializerDot,
857 => break :blk,919 => break :blk,
...@@ -948,7 +1010,7 @@ fn renderExpression(...@@ -948,7 +1010,7 @@ fn renderExpression(
9481010
949 const params = call.params();1011 const params = call.params();
950 for (params) |param_node, i| {1012 for (params) |param_node, i| {
951 const param_node_new_indent = if (param_node.id == .MultilineStringLiteral) blk: {1013 const param_node_new_indent = if (param_node.tag == .MultilineStringLiteral) blk: {
952 break :blk indent;1014 break :blk indent;
953 } else blk: {1015 } else blk: {
954 try stream.writeByteNTimes(' ', new_indent);1016 try stream.writeByteNTimes(' ', new_indent);
...@@ -1179,9 +1241,15 @@ fn renderExpression(...@@ -1179,9 +1241,15 @@ fn renderExpression(
1179 const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base);1241 const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base);
1180 return renderToken(tree, stream, error_type.token, indent, start_col, space);1242 return renderToken(tree, stream, error_type.token, indent, start_col, space);
1181 },1243 },
1182 .VarType => {1244 .AnyType => {
1183 const var_type = @fieldParentPtr(ast.Node.VarType, "base", base);1245 const any_type = @fieldParentPtr(ast.Node.AnyType, "base", base);
1184 return renderToken(tree, stream, var_type.token, indent, start_col, space);1246 if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) {
1247 // TODO remove in next release cycle
1248 try stream.writeAll("anytype");
1249 if (space == .Comma) try stream.writeAll(",\n");
1250 return;
1251 }
1252 return renderToken(tree, stream, any_type.token, indent, start_col, space);
1185 },1253 },
1186 .ContainerDecl => {1254 .ContainerDecl => {
1187 const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);1255 const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
...@@ -1252,7 +1320,7 @@ fn renderExpression(...@@ -1252,7 +1320,7 @@ fn renderExpression(
1252 // declarations inside are fields1320 // declarations inside are fields
1253 const src_has_only_fields = blk: {1321 const src_has_only_fields = blk: {
1254 for (fields_and_decls) |decl| {1322 for (fields_and_decls) |decl| {
1255 if (decl.id != .ContainerField) break :blk false;1323 if (decl.tag != .ContainerField) break :blk false;
1256 }1324 }
1257 break :blk true;1325 break :blk true;
1258 };1326 };
...@@ -1377,7 +1445,7 @@ fn renderExpression(...@@ -1377,7 +1445,7 @@ fn renderExpression(
1377 .ErrorTag => {1445 .ErrorTag => {
1378 const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base);1446 const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", base);
13791447
1380 try renderDocComments(tree, stream, tag, indent, start_col);1448 try renderDocComments(tree, stream, tag, tag.doc_comments, indent, start_col);
1381 return renderToken(tree, stream, tag.name_token, indent, start_col, space); // name1449 return renderToken(tree, stream, tag.name_token, indent, start_col, space); // name
1382 },1450 },
13831451
...@@ -1451,23 +1519,23 @@ fn renderExpression(...@@ -1451,23 +1519,23 @@ fn renderExpression(
1451 .FnProto => {1519 .FnProto => {
1452 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);1520 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
14531521
1454 if (fn_proto.visib_token) |visib_token_index| {1522 if (fn_proto.getTrailer("visib_token")) |visib_token_index| {
1455 const visib_token = tree.token_ids[visib_token_index];1523 const visib_token = tree.token_ids[visib_token_index];
1456 assert(visib_token == .Keyword_pub or visib_token == .Keyword_export);1524 assert(visib_token == .Keyword_pub or visib_token == .Keyword_export);
14571525
1458 try renderToken(tree, stream, visib_token_index, indent, start_col, Space.Space); // pub1526 try renderToken(tree, stream, visib_token_index, indent, start_col, Space.Space); // pub
1459 }1527 }
14601528
1461 if (fn_proto.extern_export_inline_token) |extern_export_inline_token| {1529 if (fn_proto.getTrailer("extern_export_inline_token")) |extern_export_inline_token| {
1462 if (!fn_proto.is_extern_prototype)1530 if (fn_proto.getTrailer("is_extern_prototype") == null)
1463 try renderToken(tree, stream, extern_export_inline_token, indent, start_col, Space.Space); // extern/export/inline1531 try renderToken(tree, stream, extern_export_inline_token, indent, start_col, Space.Space); // extern/export/inline
1464 }1532 }
14651533
1466 if (fn_proto.lib_name) |lib_name| {1534 if (fn_proto.getTrailer("lib_name")) |lib_name| {
1467 try renderExpression(allocator, stream, tree, indent, start_col, lib_name, Space.Space);1535 try renderExpression(allocator, stream, tree, indent, start_col, lib_name, Space.Space);
1468 }1536 }
14691537
1470 const lparen = if (fn_proto.name_token) |name_token| blk: {1538 const lparen = if (fn_proto.getTrailer("name_token")) |name_token| blk: {
1471 try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.Space); // fn1539 try renderToken(tree, stream, fn_proto.fn_token, indent, start_col, Space.Space); // fn
1472 try renderToken(tree, stream, name_token, indent, start_col, Space.None); // name1540 try renderToken(tree, stream, name_token, indent, start_col, Space.None); // name
1473 break :blk tree.nextToken(name_token);1541 break :blk tree.nextToken(name_token);
...@@ -1480,11 +1548,11 @@ fn renderExpression(...@@ -1480,11 +1548,11 @@ fn renderExpression(
1480 const rparen = tree.prevToken(1548 const rparen = tree.prevToken(
1481 // the first token for the annotation expressions is the left1549 // the first token for the annotation expressions is the left
1482 // parenthesis, hence the need for two prevToken1550 // parenthesis, hence the need for two prevToken
1483 if (fn_proto.align_expr) |align_expr|1551 if (fn_proto.getTrailer("align_expr")) |align_expr|
1484 tree.prevToken(tree.prevToken(align_expr.firstToken()))1552 tree.prevToken(tree.prevToken(align_expr.firstToken()))
1485 else if (fn_proto.section_expr) |section_expr|1553 else if (fn_proto.getTrailer("section_expr")) |section_expr|
1486 tree.prevToken(tree.prevToken(section_expr.firstToken()))1554 tree.prevToken(tree.prevToken(section_expr.firstToken()))
1487 else if (fn_proto.callconv_expr) |callconv_expr|1555 else if (fn_proto.getTrailer("callconv_expr")) |callconv_expr|
1488 tree.prevToken(tree.prevToken(callconv_expr.firstToken()))1556 tree.prevToken(tree.prevToken(callconv_expr.firstToken()))
1489 else switch (fn_proto.return_type) {1557 else switch (fn_proto.return_type) {
1490 .Explicit => |node| node.firstToken(),1558 .Explicit => |node| node.firstToken(),
...@@ -1505,11 +1573,14 @@ fn renderExpression(...@@ -1505,11 +1573,14 @@ fn renderExpression(
1505 for (fn_proto.params()) |param_decl, i| {1573 for (fn_proto.params()) |param_decl, i| {
1506 try renderParamDecl(allocator, stream, tree, indent, start_col, param_decl, Space.None);1574 try renderParamDecl(allocator, stream, tree, indent, start_col, param_decl, Space.None);
15071575
1508 if (i + 1 < fn_proto.params_len) {1576 if (i + 1 < fn_proto.params_len or fn_proto.getTrailer("var_args_token") != null) {
1509 const comma = tree.nextToken(param_decl.lastToken());1577 const comma = tree.nextToken(param_decl.lastToken());
1510 try renderToken(tree, stream, comma, indent, start_col, Space.Space); // ,1578 try renderToken(tree, stream, comma, indent, start_col, Space.Space); // ,
1511 }1579 }
1512 }1580 }
1581 if (fn_proto.getTrailer("var_args_token")) |var_args_token| {
1582 try renderToken(tree, stream, var_args_token, indent, start_col, Space.None);
1583 }
1513 } else {1584 } else {
1514 // one param per line1585 // one param per line
1515 const new_indent = indent + indent_delta;1586 const new_indent = indent + indent_delta;
...@@ -1519,12 +1590,16 @@ fn renderExpression(...@@ -1519,12 +1590,16 @@ fn renderExpression(
1519 try stream.writeByteNTimes(' ', new_indent);1590 try stream.writeByteNTimes(' ', new_indent);
1520 try renderParamDecl(allocator, stream, tree, new_indent, start_col, param_decl, Space.Comma);1591 try renderParamDecl(allocator, stream, tree, new_indent, start_col, param_decl, Space.Comma);
1521 }1592 }
1593 if (fn_proto.getTrailer("var_args_token")) |var_args_token| {
1594 try stream.writeByteNTimes(' ', new_indent);
1595 try renderToken(tree, stream, var_args_token, new_indent, start_col, Space.Comma);
1596 }
1522 try stream.writeByteNTimes(' ', indent);1597 try stream.writeByteNTimes(' ', indent);
1523 }1598 }
15241599
1525 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )1600 try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // )
15261601
1527 if (fn_proto.align_expr) |align_expr| {1602 if (fn_proto.getTrailer("align_expr")) |align_expr| {
1528 const align_rparen = tree.nextToken(align_expr.lastToken());1603 const align_rparen = tree.nextToken(align_expr.lastToken());
1529 const align_lparen = tree.prevToken(align_expr.firstToken());1604 const align_lparen = tree.prevToken(align_expr.firstToken());
1530 const align_kw = tree.prevToken(align_lparen);1605 const align_kw = tree.prevToken(align_lparen);
...@@ -1535,7 +1610,7 @@ fn renderExpression(...@@ -1535,7 +1610,7 @@ fn renderExpression(
1535 try renderToken(tree, stream, align_rparen, indent, start_col, Space.Space); // )1610 try renderToken(tree, stream, align_rparen, indent, start_col, Space.Space); // )
1536 }1611 }
15371612
1538 if (fn_proto.section_expr) |section_expr| {1613 if (fn_proto.getTrailer("section_expr")) |section_expr| {
1539 const section_rparen = tree.nextToken(section_expr.lastToken());1614 const section_rparen = tree.nextToken(section_expr.lastToken());
1540 const section_lparen = tree.prevToken(section_expr.firstToken());1615 const section_lparen = tree.prevToken(section_expr.firstToken());
1541 const section_kw = tree.prevToken(section_lparen);1616 const section_kw = tree.prevToken(section_lparen);
...@@ -1546,7 +1621,7 @@ fn renderExpression(...@@ -1546,7 +1621,7 @@ fn renderExpression(
1546 try renderToken(tree, stream, section_rparen, indent, start_col, Space.Space); // )1621 try renderToken(tree, stream, section_rparen, indent, start_col, Space.Space); // )
1547 }1622 }
15481623
1549 if (fn_proto.callconv_expr) |callconv_expr| {1624 if (fn_proto.getTrailer("callconv_expr")) |callconv_expr| {
1550 const callconv_rparen = tree.nextToken(callconv_expr.lastToken());1625 const callconv_rparen = tree.nextToken(callconv_expr.lastToken());
1551 const callconv_lparen = tree.prevToken(callconv_expr.firstToken());1626 const callconv_lparen = tree.prevToken(callconv_expr.firstToken());
1552 const callconv_kw = tree.prevToken(callconv_lparen);1627 const callconv_kw = tree.prevToken(callconv_lparen);
...@@ -1555,9 +1630,9 @@ fn renderExpression(...@@ -1555,9 +1630,9 @@ fn renderExpression(
1555 try renderToken(tree, stream, callconv_lparen, indent, start_col, Space.None); // (1630 try renderToken(tree, stream, callconv_lparen, indent, start_col, Space.None); // (
1556 try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None);1631 try renderExpression(allocator, stream, tree, indent, start_col, callconv_expr, Space.None);
1557 try renderToken(tree, stream, callconv_rparen, indent, start_col, Space.Space); // )1632 try renderToken(tree, stream, callconv_rparen, indent, start_col, Space.Space); // )
1558 } else if (fn_proto.is_extern_prototype) {1633 } else if (fn_proto.getTrailer("is_extern_prototype") != null) {
1559 try stream.writeAll("callconv(.C) ");1634 try stream.writeAll("callconv(.C) ");
1560 } else if (fn_proto.is_async) {1635 } else if (fn_proto.getTrailer("is_async") != null) {
1561 try stream.writeAll("callconv(.Async) ");1636 try stream.writeAll("callconv(.Async) ");
1562 }1637 }
15631638
...@@ -1792,7 +1867,7 @@ fn renderExpression(...@@ -1792,7 +1867,7 @@ fn renderExpression(
17921867
1793 const rparen = tree.nextToken(for_node.array_expr.lastToken());1868 const rparen = tree.nextToken(for_node.array_expr.lastToken());
17941869
1795 const body_is_block = for_node.body.id == .Block;1870 const body_is_block = for_node.body.tag == .Block;
1796 const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());1871 const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());
1797 const body_on_same_line = body_is_block or src_one_line_to_body;1872 const body_on_same_line = body_is_block or src_one_line_to_body;
17981873
...@@ -1835,7 +1910,7 @@ fn renderExpression(...@@ -1835,7 +1910,7 @@ fn renderExpression(
18351910
1836 try renderExpression(allocator, stream, tree, indent, start_col, if_node.condition, Space.None); // condition1911 try renderExpression(allocator, stream, tree, indent, start_col, if_node.condition, Space.None); // condition
18371912
1838 const body_is_if_block = if_node.body.id == .If;1913 const body_is_if_block = if_node.body.tag == .If;
1839 const body_is_block = nodeIsBlock(if_node.body);1914 const body_is_block = nodeIsBlock(if_node.body);
18401915
1841 if (body_is_if_block) {1916 if (body_is_if_block) {
...@@ -1939,7 +2014,7 @@ fn renderExpression(...@@ -1939,7 +2014,7 @@ fn renderExpression(
19392014
1940 const indent_once = indent + indent_delta;2015 const indent_once = indent + indent_delta;
19412016
1942 if (asm_node.template.id == .MultilineStringLiteral) {2017 if (asm_node.template.tag == .MultilineStringLiteral) {
1943 // After rendering a multiline string literal the cursor is2018 // After rendering a multiline string literal the cursor is
1944 // already offset by indent2019 // already offset by indent
1945 try stream.writeByteNTimes(' ', indent_delta);2020 try stream.writeByteNTimes(' ', indent_delta);
...@@ -2051,9 +2126,49 @@ fn renderExpression(...@@ -2051,9 +2126,49 @@ fn renderExpression(
2051 }2126 }
2052}2127}
20532128
2129fn renderArrayType(
2130 allocator: *mem.Allocator,
2131 stream: anytype,
2132 tree: *ast.Tree,
2133 indent: usize,
2134 start_col: *usize,
2135 lbracket: ast.TokenIndex,
2136 rhs: *ast.Node,
2137 len_expr: *ast.Node,
2138 opt_sentinel: ?*ast.Node,
2139 space: Space,
2140) (@TypeOf(stream).Error || Error)!void {
2141 const rbracket = tree.nextToken(if (opt_sentinel) |sentinel|
2142 sentinel.lastToken()
2143 else
2144 len_expr.lastToken());
2145
2146 try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
2147
2148 const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
2149 const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
2150 const new_indent = if (ends_with_comment) indent + indent_delta else indent;
2151 const new_space = if (ends_with_comment) Space.Newline else Space.None;
2152 try renderExpression(allocator, stream, tree, new_indent, start_col, len_expr, new_space);
2153 if (starts_with_comment) {
2154 try stream.writeByte('\n');
2155 }
2156 if (ends_with_comment or starts_with_comment) {
2157 try stream.writeByteNTimes(' ', indent);
2158 }
2159 if (opt_sentinel) |sentinel| {
2160 const colon_token = tree.prevToken(sentinel.firstToken());
2161 try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
2162 try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
2163 }
2164 try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ]
2165
2166 return renderExpression(allocator, stream, tree, indent, start_col, rhs, space);
2167}
2168
2054fn renderAsmOutput(2169fn renderAsmOutput(
2055 allocator: *mem.Allocator,2170 allocator: *mem.Allocator,
2056 stream: var,2171 stream: anytype,
2057 tree: *ast.Tree,2172 tree: *ast.Tree,
2058 indent: usize,2173 indent: usize,
2059 start_col: *usize,2174 start_col: *usize,
...@@ -2081,7 +2196,7 @@ fn renderAsmOutput(...@@ -2081,7 +2196,7 @@ fn renderAsmOutput(
20812196
2082fn renderAsmInput(2197fn renderAsmInput(
2083 allocator: *mem.Allocator,2198 allocator: *mem.Allocator,
2084 stream: var,2199 stream: anytype,
2085 tree: *ast.Tree,2200 tree: *ast.Tree,
2086 indent: usize,2201 indent: usize,
2087 start_col: *usize,2202 start_col: *usize,
...@@ -2099,70 +2214,75 @@ fn renderAsmInput(...@@ -2099,70 +2214,75 @@ fn renderAsmInput(
20992214
2100fn renderVarDecl(2215fn renderVarDecl(
2101 allocator: *mem.Allocator,2216 allocator: *mem.Allocator,
2102 stream: var,2217 stream: anytype,
2103 tree: *ast.Tree,2218 tree: *ast.Tree,
2104 indent: usize,2219 indent: usize,
2105 start_col: *usize,2220 start_col: *usize,
2106 var_decl: *ast.Node.VarDecl,2221 var_decl: *ast.Node.VarDecl,
2107) (@TypeOf(stream).Error || Error)!void {2222) (@TypeOf(stream).Error || Error)!void {
2108 if (var_decl.visib_token) |visib_token| {2223 if (var_decl.getTrailer("visib_token")) |visib_token| {
2109 try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub2224 try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub
2110 }2225 }
21112226
2112 if (var_decl.extern_export_token) |extern_export_token| {2227 if (var_decl.getTrailer("extern_export_token")) |extern_export_token| {
2113 try renderToken(tree, stream, extern_export_token, indent, start_col, Space.Space); // extern2228 try renderToken(tree, stream, extern_export_token, indent, start_col, Space.Space); // extern
21142229
2115 if (var_decl.lib_name) |lib_name| {2230 if (var_decl.getTrailer("lib_name")) |lib_name| {
2116 try renderExpression(allocator, stream, tree, indent, start_col, lib_name, Space.Space); // "lib"2231 try renderExpression(allocator, stream, tree, indent, start_col, lib_name, Space.Space); // "lib"
2117 }2232 }
2118 }2233 }
21192234
2120 if (var_decl.comptime_token) |comptime_token| {2235 if (var_decl.getTrailer("comptime_token")) |comptime_token| {
2121 try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space); // comptime2236 try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space); // comptime
2122 }2237 }
21232238
2124 if (var_decl.thread_local_token) |thread_local_token| {2239 if (var_decl.getTrailer("thread_local_token")) |thread_local_token| {
2125 try renderToken(tree, stream, thread_local_token, indent, start_col, Space.Space); // threadlocal2240 try renderToken(tree, stream, thread_local_token, indent, start_col, Space.Space); // threadlocal
2126 }2241 }
2127 try renderToken(tree, stream, var_decl.mut_token, indent, start_col, Space.Space); // var2242 try renderToken(tree, stream, var_decl.mut_token, indent, start_col, Space.Space); // var
21282243
2129 const name_space = if (var_decl.type_node == null and (var_decl.align_node != null or2244 const name_space = if (var_decl.getTrailer("type_node") == null and
2130 var_decl.section_node != null or var_decl.init_node != null)) Space.Space else Space.None;2245 (var_decl.getTrailer("align_node") != null or
2246 var_decl.getTrailer("section_node") != null or
2247 var_decl.getTrailer("init_node") != null))
2248 Space.Space
2249 else
2250 Space.None;
2131 try renderToken(tree, stream, var_decl.name_token, indent, start_col, name_space);2251 try renderToken(tree, stream, var_decl.name_token, indent, start_col, name_space);
21322252
2133 if (var_decl.type_node) |type_node| {2253 if (var_decl.getTrailer("type_node")) |type_node| {
2134 try renderToken(tree, stream, tree.nextToken(var_decl.name_token), indent, start_col, Space.Space);2254 try renderToken(tree, stream, tree.nextToken(var_decl.name_token), indent, start_col, Space.Space);
2135 const s = if (var_decl.align_node != null or2255 const s = if (var_decl.getTrailer("align_node") != null or
2136 var_decl.section_node != null or2256 var_decl.getTrailer("section_node") != null or
2137 var_decl.init_node != null) Space.Space else Space.None;2257 var_decl.getTrailer("init_node") != null) Space.Space else Space.None;
2138 try renderExpression(allocator, stream, tree, indent, start_col, type_node, s);2258 try renderExpression(allocator, stream, tree, indent, start_col, type_node, s);
2139 }2259 }
21402260
2141 if (var_decl.align_node) |align_node| {2261 if (var_decl.getTrailer("align_node")) |align_node| {
2142 const lparen = tree.prevToken(align_node.firstToken());2262 const lparen = tree.prevToken(align_node.firstToken());
2143 const align_kw = tree.prevToken(lparen);2263 const align_kw = tree.prevToken(lparen);
2144 const rparen = tree.nextToken(align_node.lastToken());2264 const rparen = tree.nextToken(align_node.lastToken());
2145 try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align2265 try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align
2146 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (2266 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
2147 try renderExpression(allocator, stream, tree, indent, start_col, align_node, Space.None);2267 try renderExpression(allocator, stream, tree, indent, start_col, align_node, Space.None);
2148 const s = if (var_decl.section_node != null or var_decl.init_node != null) Space.Space else Space.None;2268 const s = if (var_decl.getTrailer("section_node") != null or var_decl.getTrailer("init_node") != null) Space.Space else Space.None;
2149 try renderToken(tree, stream, rparen, indent, start_col, s); // )2269 try renderToken(tree, stream, rparen, indent, start_col, s); // )
2150 }2270 }
21512271
2152 if (var_decl.section_node) |section_node| {2272 if (var_decl.getTrailer("section_node")) |section_node| {
2153 const lparen = tree.prevToken(section_node.firstToken());2273 const lparen = tree.prevToken(section_node.firstToken());
2154 const section_kw = tree.prevToken(lparen);2274 const section_kw = tree.prevToken(lparen);
2155 const rparen = tree.nextToken(section_node.lastToken());2275 const rparen = tree.nextToken(section_node.lastToken());
2156 try renderToken(tree, stream, section_kw, indent, start_col, Space.None); // linksection2276 try renderToken(tree, stream, section_kw, indent, start_col, Space.None); // linksection
2157 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (2277 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
2158 try renderExpression(allocator, stream, tree, indent, start_col, section_node, Space.None);2278 try renderExpression(allocator, stream, tree, indent, start_col, section_node, Space.None);
2159 const s = if (var_decl.init_node != null) Space.Space else Space.None;2279 const s = if (var_decl.getTrailer("init_node") != null) Space.Space else Space.None;
2160 try renderToken(tree, stream, rparen, indent, start_col, s); // )2280 try renderToken(tree, stream, rparen, indent, start_col, s); // )
2161 }2281 }
21622282
2163 if (var_decl.init_node) |init_node| {2283 if (var_decl.getTrailer("init_node")) |init_node| {
2164 const s = if (init_node.id == .MultilineStringLiteral) Space.None else Space.Space;2284 const s = if (init_node.tag == .MultilineStringLiteral) Space.None else Space.Space;
2165 try renderToken(tree, stream, var_decl.eq_token.?, indent, start_col, s); // =2285 try renderToken(tree, stream, var_decl.getTrailer("eq_token").?, indent, start_col, s); // =
2166 try renderExpression(allocator, stream, tree, indent, start_col, init_node, Space.None);2286 try renderExpression(allocator, stream, tree, indent, start_col, init_node, Space.None);
2167 }2287 }
21682288
...@@ -2171,14 +2291,14 @@ fn renderVarDecl(...@@ -2171,14 +2291,14 @@ fn renderVarDecl(
21712291
2172fn renderParamDecl(2292fn renderParamDecl(
2173 allocator: *mem.Allocator,2293 allocator: *mem.Allocator,
2174 stream: var,2294 stream: anytype,
2175 tree: *ast.Tree,2295 tree: *ast.Tree,
2176 indent: usize,2296 indent: usize,
2177 start_col: *usize,2297 start_col: *usize,
2178 param_decl: ast.Node.FnProto.ParamDecl,2298 param_decl: ast.Node.FnProto.ParamDecl,
2179 space: Space,2299 space: Space,
2180) (@TypeOf(stream).Error || Error)!void {2300) (@TypeOf(stream).Error || Error)!void {
2181 try renderDocComments(tree, stream, param_decl, indent, start_col);2301 try renderDocComments(tree, stream, param_decl, param_decl.doc_comments, indent, start_col);
21822302
2183 if (param_decl.comptime_token) |comptime_token| {2303 if (param_decl.comptime_token) |comptime_token| {
2184 try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space);2304 try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space);
...@@ -2191,20 +2311,19 @@ fn renderParamDecl(...@@ -2191,20 +2311,19 @@ fn renderParamDecl(
2191 try renderToken(tree, stream, tree.nextToken(name_token), indent, start_col, Space.Space); // :2311 try renderToken(tree, stream, tree.nextToken(name_token), indent, start_col, Space.Space); // :
2192 }2312 }
2193 switch (param_decl.param_type) {2313 switch (param_decl.param_type) {
2194 .var_args => |token| try renderToken(tree, stream, token, indent, start_col, space),2314 .any_type, .type_expr => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, space),
2195 .var_type, .type_expr => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, space),
2196 }2315 }
2197}2316}
21982317
2199fn renderStatement(2318fn renderStatement(
2200 allocator: *mem.Allocator,2319 allocator: *mem.Allocator,
2201 stream: var,2320 stream: anytype,
2202 tree: *ast.Tree,2321 tree: *ast.Tree,
2203 indent: usize,2322 indent: usize,
2204 start_col: *usize,2323 start_col: *usize,
2205 base: *ast.Node,2324 base: *ast.Node,
2206) (@TypeOf(stream).Error || Error)!void {2325) (@TypeOf(stream).Error || Error)!void {
2207 switch (base.id) {2326 switch (base.tag) {
2208 .VarDecl => {2327 .VarDecl => {
2209 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);2328 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
2210 try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl);2329 try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl);
...@@ -2236,7 +2355,7 @@ const Space = enum {...@@ -2236,7 +2355,7 @@ const Space = enum {
22362355
2237fn renderTokenOffset(2356fn renderTokenOffset(
2238 tree: *ast.Tree,2357 tree: *ast.Tree,
2239 stream: var,2358 stream: anytype,
2240 token_index: ast.TokenIndex,2359 token_index: ast.TokenIndex,
2241 indent: usize,2360 indent: usize,
2242 start_col: *usize,2361 start_col: *usize,
...@@ -2434,7 +2553,7 @@ fn renderTokenOffset(...@@ -2434,7 +2553,7 @@ fn renderTokenOffset(
24342553
2435fn renderToken(2554fn renderToken(
2436 tree: *ast.Tree,2555 tree: *ast.Tree,
2437 stream: var,2556 stream: anytype,
2438 token_index: ast.TokenIndex,2557 token_index: ast.TokenIndex,
2439 indent: usize,2558 indent: usize,
2440 start_col: *usize,2559 start_col: *usize,
...@@ -2445,18 +2564,19 @@ fn renderToken(...@@ -2445,18 +2564,19 @@ fn renderToken(
24452564
2446fn renderDocComments(2565fn renderDocComments(
2447 tree: *ast.Tree,2566 tree: *ast.Tree,
2448 stream: var,2567 stream: anytype,
2449 node: var,2568 node: anytype,
2569 doc_comments: ?*ast.Node.DocComment,
2450 indent: usize,2570 indent: usize,
2451 start_col: *usize,2571 start_col: *usize,
2452) (@TypeOf(stream).Error || Error)!void {2572) (@TypeOf(stream).Error || Error)!void {
2453 const comment = node.doc_comments orelse return;2573 const comment = doc_comments orelse return;
2454 return renderDocCommentsToken(tree, stream, comment, node.firstToken(), indent, start_col);2574 return renderDocCommentsToken(tree, stream, comment, node.firstToken(), indent, start_col);
2455}2575}
24562576
2457fn renderDocCommentsToken(2577fn renderDocCommentsToken(
2458 tree: *ast.Tree,2578 tree: *ast.Tree,
2459 stream: var,2579 stream: anytype,
2460 comment: *ast.Node.DocComment,2580 comment: *ast.Node.DocComment,
2461 first_token: ast.TokenIndex,2581 first_token: ast.TokenIndex,
2462 indent: usize,2582 indent: usize,
...@@ -2482,7 +2602,7 @@ fn renderDocCommentsToken(...@@ -2482,7 +2602,7 @@ fn renderDocCommentsToken(
2482}2602}
24832603
2484fn nodeIsBlock(base: *const ast.Node) bool {2604fn nodeIsBlock(base: *const ast.Node) bool {
2485 return switch (base.id) {2605 return switch (base.tag) {
2486 .Block,2606 .Block,
2487 .If,2607 .If,
2488 .For,2608 .For,
...@@ -2494,10 +2614,52 @@ fn nodeIsBlock(base: *const ast.Node) bool {...@@ -2494,10 +2614,52 @@ fn nodeIsBlock(base: *const ast.Node) bool {
2494}2614}
24952615
2496fn nodeCausesSliceOpSpace(base: *ast.Node) bool {2616fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
2497 const infix_op = base.cast(ast.Node.InfixOp) orelse return false;2617 return switch (base.tag) {
2498 return switch (infix_op.op) {2618 .Catch,
2499 ast.Node.InfixOp.Op.Period => false,2619 .Add,
2500 else => true,2620 .AddWrap,
2621 .ArrayCat,
2622 .ArrayMult,
2623 .Assign,
2624 .AssignBitAnd,
2625 .AssignBitOr,
2626 .AssignBitShiftLeft,
2627 .AssignBitShiftRight,
2628 .AssignBitXor,
2629 .AssignDiv,
2630 .AssignSub,
2631 .AssignSubWrap,
2632 .AssignMod,
2633 .AssignAdd,
2634 .AssignAddWrap,
2635 .AssignMul,
2636 .AssignMulWrap,
2637 .BangEqual,
2638 .BitAnd,
2639 .BitOr,
2640 .BitShiftLeft,
2641 .BitShiftRight,
2642 .BitXor,
2643 .BoolAnd,
2644 .BoolOr,
2645 .Div,
2646 .EqualEqual,
2647 .ErrorUnion,
2648 .GreaterOrEqual,
2649 .GreaterThan,
2650 .LessOrEqual,
2651 .LessThan,
2652 .MergeErrorSets,
2653 .Mod,
2654 .Mul,
2655 .MulWrap,
2656 .Range,
2657 .Sub,
2658 .SubWrap,
2659 .UnwrapOptional,
2660 => true,
2661
2662 else => false,
2501 };2663 };
2502}2664}
25032665
...@@ -2532,7 +2694,7 @@ const FindByteOutStream = struct {...@@ -2532,7 +2694,7 @@ const FindByteOutStream = struct {
2532 }2694 }
2533};2695};
25342696
2535fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Error!void {2697fn copyFixingWhitespace(stream: anytype, slice: []const u8) @TypeOf(stream).Error!void {
2536 for (slice) |byte| switch (byte) {2698 for (slice) |byte| switch (byte) {
2537 '\t' => try stream.writeAll(" "),2699 '\t' => try stream.writeAll(" "),
2538 '\r' => {},2700 '\r' => {},
lib/std/zig/string_literal.zig+1-1
...@@ -125,7 +125,7 @@ test "parse" {...@@ -125,7 +125,7 @@ test "parse" {
125}125}
126126
127/// Writes a Zig-syntax escaped string literal to the stream. Includes the double quotes.127/// Writes a Zig-syntax escaped string literal to the stream. Includes the double quotes.
128pub fn render(utf8: []const u8, out_stream: var) !void {128pub fn render(utf8: []const u8, out_stream: anytype) !void {
129 try out_stream.writeByte('"');129 try out_stream.writeByte('"');
130 for (utf8) |byte| switch (byte) {130 for (utf8) |byte| switch (byte) {
131 '\n' => try out_stream.writeAll("\\n"),131 '\n' => try out_stream.writeAll("\\n"),
lib/std/zig/system.zig+6-5
...@@ -130,7 +130,7 @@ pub const NativePaths = struct {...@@ -130,7 +130,7 @@ pub const NativePaths = struct {
130 return self.appendArray(&self.include_dirs, s);130 return self.appendArray(&self.include_dirs, s);
131 }131 }
132132
133 pub fn addIncludeDirFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void {133 pub fn addIncludeDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
134 const item = try std.fmt.allocPrint0(self.include_dirs.allocator, fmt, args);134 const item = try std.fmt.allocPrint0(self.include_dirs.allocator, fmt, args);
135 errdefer self.include_dirs.allocator.free(item);135 errdefer self.include_dirs.allocator.free(item);
136 try self.include_dirs.append(item);136 try self.include_dirs.append(item);
...@@ -140,7 +140,7 @@ pub const NativePaths = struct {...@@ -140,7 +140,7 @@ pub const NativePaths = struct {
140 return self.appendArray(&self.lib_dirs, s);140 return self.appendArray(&self.lib_dirs, s);
141 }141 }
142142
143 pub fn addLibDirFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void {143 pub fn addLibDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
144 const item = try std.fmt.allocPrint0(self.lib_dirs.allocator, fmt, args);144 const item = try std.fmt.allocPrint0(self.lib_dirs.allocator, fmt, args);
145 errdefer self.lib_dirs.allocator.free(item);145 errdefer self.lib_dirs.allocator.free(item);
146 try self.lib_dirs.append(item);146 try self.lib_dirs.append(item);
...@@ -150,7 +150,7 @@ pub const NativePaths = struct {...@@ -150,7 +150,7 @@ pub const NativePaths = struct {
150 return self.appendArray(&self.warnings, s);150 return self.appendArray(&self.warnings, s);
151 }151 }
152152
153 pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: var) !void {153 pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void {
154 const item = try std.fmt.allocPrint0(self.warnings.allocator, fmt, args);154 const item = try std.fmt.allocPrint0(self.warnings.allocator, fmt, args);
155 errdefer self.warnings.allocator.free(item);155 errdefer self.warnings.allocator.free(item);
156 try self.warnings.append(item);156 try self.warnings.append(item);
...@@ -161,7 +161,7 @@ pub const NativePaths = struct {...@@ -161,7 +161,7 @@ pub const NativePaths = struct {
161 }161 }
162162
163 fn appendArray(self: *NativePaths, array: *ArrayList([:0]u8), s: []const u8) !void {163 fn appendArray(self: *NativePaths, array: *ArrayList([:0]u8), s: []const u8) !void {
164 const item = try std.mem.dupeZ(array.allocator, u8, s);164 const item = try array.allocator.dupeZ(u8, s);
165 errdefer array.allocator.free(item);165 errdefer array.allocator.free(item);
166 try array.append(item);166 try array.append(item);
167 }167 }
...@@ -859,6 +859,7 @@ pub const NativeTargetInfo = struct {...@@ -859,6 +859,7 @@ pub const NativeTargetInfo = struct {
859 error.ConnectionTimedOut => return error.UnableToReadElfFile,859 error.ConnectionTimedOut => return error.UnableToReadElfFile,
860 error.Unexpected => return error.Unexpected,860 error.Unexpected => return error.Unexpected,
861 error.InputOutput => return error.FileSystem,861 error.InputOutput => return error.FileSystem,
862 error.AccessDenied => return error.Unexpected,
862 };863 };
863 if (len == 0) return error.UnexpectedEndOfFile;864 if (len == 0) return error.UnexpectedEndOfFile;
864 i += len;865 i += len;
...@@ -886,7 +887,7 @@ pub const NativeTargetInfo = struct {...@@ -886,7 +887,7 @@ pub const NativeTargetInfo = struct {
886 abi: Target.Abi,887 abi: Target.Abi,
887 };888 };
888889
889 pub fn elfInt(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) {890 pub fn elfInt(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
890 if (is_64) {891 if (is_64) {
891 if (need_bswap) {892 if (need_bswap) {
892 return @byteSwap(@TypeOf(int_64), int_64);893 return @byteSwap(@TypeOf(int_64), int_64);
lib/std/zig/tokenizer.zig+4-1
...@@ -15,6 +15,7 @@ pub const Token = struct {...@@ -15,6 +15,7 @@ pub const Token = struct {
15 .{ "allowzero", .Keyword_allowzero },15 .{ "allowzero", .Keyword_allowzero },
16 .{ "and", .Keyword_and },16 .{ "and", .Keyword_and },
17 .{ "anyframe", .Keyword_anyframe },17 .{ "anyframe", .Keyword_anyframe },
18 .{ "anytype", .Keyword_anytype },
18 .{ "asm", .Keyword_asm },19 .{ "asm", .Keyword_asm },
19 .{ "async", .Keyword_async },20 .{ "async", .Keyword_async },
20 .{ "await", .Keyword_await },21 .{ "await", .Keyword_await },
...@@ -140,6 +141,8 @@ pub const Token = struct {...@@ -140,6 +141,8 @@ pub const Token = struct {
140 Keyword_align,141 Keyword_align,
141 Keyword_allowzero,142 Keyword_allowzero,
142 Keyword_and,143 Keyword_and,
144 Keyword_anyframe,
145 Keyword_anytype,
143 Keyword_asm,146 Keyword_asm,
144 Keyword_async,147 Keyword_async,
145 Keyword_await,148 Keyword_await,
...@@ -168,7 +171,6 @@ pub const Token = struct {...@@ -168,7 +171,6 @@ pub const Token = struct {
168 Keyword_or,171 Keyword_or,
169 Keyword_orelse,172 Keyword_orelse,
170 Keyword_packed,173 Keyword_packed,
171 Keyword_anyframe,
172 Keyword_pub,174 Keyword_pub,
173 Keyword_resume,175 Keyword_resume,
174 Keyword_return,176 Keyword_return,
...@@ -263,6 +265,7 @@ pub const Token = struct {...@@ -263,6 +265,7 @@ pub const Token = struct {
263 .Keyword_allowzero => "allowzero",265 .Keyword_allowzero => "allowzero",
264 .Keyword_and => "and",266 .Keyword_and => "and",
265 .Keyword_anyframe => "anyframe",267 .Keyword_anyframe => "anyframe",
268 .Keyword_anytype => "anytype",
266 .Keyword_asm => "asm",269 .Keyword_asm => "asm",
267 .Keyword_async => "async",270 .Keyword_async => "async",
268 .Keyword_await => "await",271 .Keyword_await => "await",
src-self-hosted/Module.zig+1667-683
...@@ -15,30 +15,39 @@ const ir = @import("ir.zig");...@@ -15,30 +15,39 @@ const ir = @import("ir.zig");
15const zir = @import("zir.zig");15const zir = @import("zir.zig");
16const Module = @This();16const Module = @This();
17const Inst = ir.Inst;17const Inst = ir.Inst;
1818const Body = ir.Body;
19/// General-purpose allocator.19const ast = std.zig.ast;
20allocator: *Allocator,20const trace = @import("tracy.zig").trace;
21const liveness = @import("liveness.zig");
22const astgen = @import("astgen.zig");
23
24/// General-purpose allocator. Used for both temporary and long-term storage.
25gpa: *Allocator,
21/// Pointer to externally managed resource.26/// Pointer to externally managed resource.
22root_pkg: *Package,27root_pkg: *Package,
23/// Module owns this resource.28/// Module owns this resource.
24root_scope: *Scope.ZIRModule,29/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
25bin_file: link.ElfFile,30root_scope: *Scope,
31bin_file: *link.File,
26bin_file_dir: std.fs.Dir,32bin_file_dir: std.fs.Dir,
27bin_file_path: []const u8,33bin_file_path: []const u8,
28/// It's rare for a decl to be exported, so we save memory by having a sparse map of34/// It's rare for a decl to be exported, so we save memory by having a sparse map of
29/// Decl pointers to details about them being exported.35/// Decl pointers to details about them being exported.
30/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.36/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
31decl_exports: std.AutoHashMap(*Decl, []*Export),37decl_exports: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
38/// We track which export is associated with the given symbol name for quick
39/// detection of symbol collisions.
40symbol_exports: std.StringHashMapUnmanaged(*Export) = .{},
32/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl41/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
33/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that42/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
34/// is performing the export of another Decl.43/// is performing the export of another Decl.
35/// This table owns the Export memory.44/// This table owns the Export memory.
36export_owners: std.AutoHashMap(*Decl, []*Export),45export_owners: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
37/// Maps fully qualified namespaced names to the Decl struct for them.46/// Maps fully qualified namespaced names to the Decl struct for them.
38decl_table: std.AutoHashMap(Decl.Hash, *Decl),47decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
3948
40optimize_mode: std.builtin.Mode,49optimize_mode: std.builtin.Mode,
41link_error_flags: link.ElfFile.ErrorFlags = link.ElfFile.ErrorFlags{},50link_error_flags: link.File.ErrorFlags = .{},
4251
43work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),52work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
4453
...@@ -47,28 +56,36 @@ work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),...@@ -47,28 +56,36 @@ work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
47/// The ErrorMsg memory is owned by the decl, using Module's allocator.56/// The ErrorMsg memory is owned by the decl, using Module's allocator.
48/// Note that a Decl can succeed but the Fn it represents can fail. In this case,57/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
49/// a Decl can have a failed_decls entry but have analysis status of success.58/// a Decl can have a failed_decls entry but have analysis status of success.
50failed_decls: std.AutoHashMap(*Decl, *ErrorMsg),59failed_decls: std.AutoHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
51/// Using a map here for consistency with the other fields here.60/// Using a map here for consistency with the other fields here.
52/// The ErrorMsg memory is owned by the `Scope.ZIRModule`, using Module's allocator.61/// The ErrorMsg memory is owned by the `Scope`, using Module's allocator.
53failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg),62failed_files: std.AutoHashMapUnmanaged(*Scope, *ErrorMsg) = .{},
54/// Using a map here for consistency with the other fields here.63/// Using a map here for consistency with the other fields here.
55/// The ErrorMsg memory is owned by the `Export`, using Module's allocator.64/// The ErrorMsg memory is owned by the `Export`, using Module's allocator.
56failed_exports: std.AutoHashMap(*Export, *ErrorMsg),65failed_exports: std.AutoHashMapUnmanaged(*Export, *ErrorMsg) = .{},
5766
58/// Incrementing integer used to compare against the corresponding Decl67/// Incrementing integer used to compare against the corresponding Decl
59/// field to determine whether a Decl's status applies to an ongoing update, or a68/// field to determine whether a Decl's status applies to an ongoing update, or a
60/// previous analysis.69/// previous analysis.
61generation: u32 = 0,70generation: u32 = 0,
6271
72next_anon_name_index: usize = 0,
73
63/// Candidates for deletion. After a semantic analysis update completes, this list74/// Candidates for deletion. After a semantic analysis update completes, this list
64/// contains Decls that need to be deleted if they end up having no references to them.75/// contains Decls that need to be deleted if they end up having no references to them.
65deletion_set: std.ArrayListUnmanaged(*Decl) = std.ArrayListUnmanaged(*Decl){},76deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
77
78keep_source_files_loaded: bool,
6679
67pub const WorkItem = union(enum) {80pub const InnerError = error{ OutOfMemory, AnalysisFail };
81
82const WorkItem = union(enum) {
68 /// Write the machine code for a Decl to the output file.83 /// Write the machine code for a Decl to the output file.
69 codegen_decl: *Decl,84 codegen_decl: *Decl,
70 /// Decl has been determined to be outdated; perform semantic analysis again.85 /// The Decl needs to be analyzed and possibly export itself.
71 re_analyze_decl: *Decl,86 /// It may have already be analyzed, or it may have been determined
87 /// to be outdated; in this case perform semantic analysis again.
88 analyze_decl: *Decl,
72};89};
7390
74pub const Export = struct {91pub const Export = struct {
...@@ -76,7 +93,7 @@ pub const Export = struct {...@@ -76,7 +93,7 @@ pub const Export = struct {
76 /// Byte offset into the file that contains the export directive.93 /// Byte offset into the file that contains the export directive.
77 src: usize,94 src: usize,
78 /// Represents the position of the export, if any, in the output file.95 /// Represents the position of the export, if any, in the output file.
79 link: link.ElfFile.Export,96 link: link.File.Elf.Export,
80 /// The Decl that performs the export. Note that this is *not* the Decl being exported.97 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
81 owner_decl: *Decl,98 owner_decl: *Decl,
82 /// The Decl being exported. Note this is *not* the Decl performing the export.99 /// The Decl being exported. Note this is *not* the Decl performing the export.
...@@ -99,13 +116,12 @@ pub const Decl = struct {...@@ -99,13 +116,12 @@ pub const Decl = struct {
99 /// mapping them to an address in the output file.116 /// mapping them to an address in the output file.
100 /// Memory owned by this decl, using Module's allocator.117 /// Memory owned by this decl, using Module's allocator.
101 name: [*:0]const u8,118 name: [*:0]const u8,
102 /// The direct parent container of the Decl. This field will need to get more fleshed out when119 /// The direct parent container of the Decl. This is either a `Scope.File` or `Scope.ZIRModule`.
103 /// self-hosted supports proper struct types and Zig AST => ZIR.
104 /// Reference to externally owned memory.120 /// Reference to externally owned memory.
105 scope: *Scope.ZIRModule,121 scope: *Scope,
106 /// Byte offset into the source file that contains this declaration.122 /// The AST Node decl index or ZIR Inst index that contains this declaration.
107 /// This is the base offset that src offsets within this Decl are relative to.123 /// Must be recomputed when the corresponding source file is modified.
108 src: usize,124 src_index: usize,
109 /// The most recent value of the Decl after a successful semantic analysis.125 /// The most recent value of the Decl after a successful semantic analysis.
110 typed_value: union(enum) {126 typed_value: union(enum) {
111 never_succeeded: void,127 never_succeeded: void,
...@@ -116,6 +132,9 @@ pub const Decl = struct {...@@ -116,6 +132,9 @@ pub const Decl = struct {
116 /// analysis of the function body is performed with this value set to `success`. Functions132 /// analysis of the function body is performed with this value set to `success`. Functions
117 /// have their own analysis status field.133 /// have their own analysis status field.
118 analysis: enum {134 analysis: enum {
135 /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore
136 /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced.
137 unreferenced,
119 /// Semantic analysis for this Decl is running right now. This state detects dependency loops.138 /// Semantic analysis for this Decl is running right now. This state detects dependency loops.
120 in_progress,139 in_progress,
121 /// This Decl might be OK but it depends on another one which did not successfully complete140 /// This Decl might be OK but it depends on another one which did not successfully complete
...@@ -125,6 +144,10 @@ pub const Decl = struct {...@@ -125,6 +144,10 @@ pub const Decl = struct {
125 /// There will be a corresponding ErrorMsg in Module.failed_decls.144 /// There will be a corresponding ErrorMsg in Module.failed_decls.
126 sema_failure,145 sema_failure,
127 /// There will be a corresponding ErrorMsg in Module.failed_decls.146 /// There will be a corresponding ErrorMsg in Module.failed_decls.
147 /// This indicates the failure was something like running out of disk space,
148 /// and attempting semantic analysis again may succeed.
149 sema_failure_retryable,
150 /// There will be a corresponding ErrorMsg in Module.failed_decls.
128 codegen_failure,151 codegen_failure,
129 /// There will be a corresponding ErrorMsg in Module.failed_decls.152 /// There will be a corresponding ErrorMsg in Module.failed_decls.
130 /// This indicates the failure was something like running out of disk space,153 /// This indicates the failure was something like running out of disk space,
...@@ -148,49 +171,54 @@ pub const Decl = struct {...@@ -148,49 +171,54 @@ pub const Decl = struct {
148171
149 /// Represents the position of the code in the output file.172 /// Represents the position of the code in the output file.
150 /// This is populated regardless of semantic analysis and code generation.173 /// This is populated regardless of semantic analysis and code generation.
151 link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,174 link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,
152175
153 contents_hash: Hash,176 contents_hash: std.zig.SrcHash,
154177
155 /// The shallow set of other decls whose typed_value could possibly change if this Decl's178 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
156 /// typed_value is modified.179 /// typed_value is modified.
157 dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},180 dependants: DepsTable = .{},
158 /// The shallow set of other decls whose typed_value changing indicates that this Decl's181 /// The shallow set of other decls whose typed_value changing indicates that this Decl's
159 /// typed_value may need to be regenerated.182 /// typed_value may need to be regenerated.
160 dependencies: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){},183 dependencies: DepsTable = .{},
184
185 /// The reason this is not `std.AutoHashMapUnmanaged` is a workaround for
186 /// stage1 compiler giving me: `error: struct 'Module.Decl' depends on itself`
187 pub const DepsTable = std.HashMapUnmanaged(*Decl, void, std.hash_map.getAutoHashFn(*Decl), std.hash_map.getAutoEqlFn(*Decl), false);
161188
162 pub fn destroy(self: *Decl, allocator: *Allocator) void {189 pub fn destroy(self: *Decl, gpa: *Allocator) void {
163 allocator.free(mem.spanZ(self.name));190 gpa.free(mem.spanZ(self.name));
164 if (self.typedValueManaged()) |tvm| {191 if (self.typedValueManaged()) |tvm| {
165 tvm.deinit(allocator);192 tvm.deinit(gpa);
166 }193 }
167 self.dependants.deinit(allocator);194 self.dependants.deinit(gpa);
168 self.dependencies.deinit(allocator);195 self.dependencies.deinit(gpa);
169 allocator.destroy(self);196 gpa.destroy(self);
170 }197 }
171198
172 pub const Hash = [16]u8;199 pub fn src(self: Decl) usize {
173200 switch (self.scope.tag) {
174 /// If the name is small enough, it is used directly as the hash.201 .file => {
175 /// If it is long, blake3 hash is computed.202 const file = @fieldParentPtr(Scope.File, "base", self.scope);
176 pub fn hashSimpleName(name: []const u8) Hash {203 const tree = file.contents.tree;
177 var out: Hash = undefined;204 const decl_node = tree.root_node.decls()[self.src_index];
178 if (name.len <= Hash.len) {205 return tree.token_locs[decl_node.firstToken()].start;
179 mem.copy(u8, &out, name);206 },
180 mem.set(u8, out[name.len..], 0);207 .zir_module => {
181 } else {208 const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope);
182 std.crypto.Blake3.hash(name, &out);209 const module = zir_module.contents.module;
210 const src_decl = module.decls[self.src_index];
211 return src_decl.inst.src;
212 },
213 .block => unreachable,
214 .gen_zir => unreachable,
215 .local_var => unreachable,
216 .decl => unreachable,
183 }217 }
184 return out;
185 }218 }
186219
187 /// Must generate unique bytes with no collisions with other decls.220 pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
188 /// The point of hashing here is only to limit the number of bytes of221 return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name));
189 /// the unique identifier to a fixed size (16 bytes).
190 pub fn fullyQualifiedNameHash(self: Decl) Hash {
191 // Right now we only have ZIRModule as the source. So this is simply the
192 // relative name of the decl.
193 return hashSimpleName(mem.spanZ(self.name));
194 }222 }
195223
196 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {224 pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
...@@ -225,34 +253,20 @@ pub const Decl = struct {...@@ -225,34 +253,20 @@ pub const Decl = struct {
225 }253 }
226254
227 fn removeDependant(self: *Decl, other: *Decl) void {255 fn removeDependant(self: *Decl, other: *Decl) void {
228 for (self.dependants.items) |item, i| {256 self.dependants.removeAssertDiscard(other);
229 if (item == other) {
230 _ = self.dependants.swapRemove(i);
231 return;
232 }
233 }
234 unreachable;
235 }257 }
236258
237 fn removeDependency(self: *Decl, other: *Decl) void {259 fn removeDependency(self: *Decl, other: *Decl) void {
238 for (self.dependencies.items) |item, i| {260 self.dependencies.removeAssertDiscard(other);
239 if (item == other) {
240 _ = self.dependencies.swapRemove(i);
241 return;
242 }
243 }
244 unreachable;
245 }261 }
246};262};
247263
248/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.264/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
249pub const Fn = struct {265pub const Fn = struct {
250 /// This memory owned by the Decl's TypedValue.Managed arena allocator.266 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
251 fn_type: Type,
252 analysis: union(enum) {267 analysis: union(enum) {
253 /// The value is the source instruction.268 queued: *ZIR,
254 queued: *zir.Inst.Fn,269 in_progress,
255 in_progress: *Analysis,
256 /// There will be a corresponding ErrorMsg in Module.failed_decls270 /// There will be a corresponding ErrorMsg in Module.failed_decls
257 sema_failure,271 sema_failure,
258 /// This Fn might be OK but it depends on another Decl which did not successfully complete272 /// This Fn might be OK but it depends on another Decl which did not successfully complete
...@@ -266,16 +280,20 @@ pub const Fn = struct {...@@ -266,16 +280,20 @@ pub const Fn = struct {
266 /// of Fn analysis.280 /// of Fn analysis.
267 pub const Analysis = struct {281 pub const Analysis = struct {
268 inner_block: Scope.Block,282 inner_block: Scope.Block,
269 /// TODO Performance optimization idea: instead of this inst_table,283 };
270 /// use a field in the zir.Inst instead to track corresponding instructions284
271 inst_table: std.AutoHashMap(*zir.Inst, *Inst),285 /// Contains un-analyzed ZIR instructions generated from Zig source AST.
272 needed_inst_capacity: usize,286 pub const ZIR = struct {
287 body: zir.Module.Body,
288 arena: std.heap.ArenaAllocator.State,
273 };289 };
274};290};
275291
276pub const Scope = struct {292pub const Scope = struct {
277 tag: Tag,293 tag: Tag,
278294
295 pub const NameHash = [16]u8;
296
279 pub fn cast(base: *Scope, comptime T: type) ?*T {297 pub fn cast(base: *Scope, comptime T: type) ?*T {
280 if (base.tag != T.base_tag)298 if (base.tag != T.base_tag)
281 return null;299 return null;
...@@ -289,30 +307,76 @@ pub const Scope = struct {...@@ -289,30 +307,76 @@ pub const Scope = struct {
289 switch (self.tag) {307 switch (self.tag) {
290 .block => return self.cast(Block).?.arena,308 .block => return self.cast(Block).?.arena,
291 .decl => return &self.cast(DeclAnalysis).?.arena.allocator,309 .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
310 .gen_zir => return self.cast(GenZIR).?.arena,
311 .local_var => return self.cast(LocalVar).?.gen_zir.arena,
292 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,312 .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
313 .file => unreachable,
293 }314 }
294 }315 }
295316
296 /// Asserts the scope has a parent which is a DeclAnalysis and317 /// If the scope has a parent which is a `DeclAnalysis`,
297 /// returns the Decl.318 /// returns the `Decl`, otherwise returns `null`.
298 pub fn decl(self: *Scope) ?*Decl {319 pub fn decl(self: *Scope) ?*Decl {
299 return switch (self.tag) {320 return switch (self.tag) {
300 .block => self.cast(Block).?.decl,321 .block => self.cast(Block).?.decl,
322 .gen_zir => self.cast(GenZIR).?.decl,
323 .local_var => return self.cast(LocalVar).?.gen_zir.decl,
301 .decl => self.cast(DeclAnalysis).?.decl,324 .decl => self.cast(DeclAnalysis).?.decl,
302 .zir_module => null,325 .zir_module => null,
326 .file => null,
303 };327 };
304 }328 }
305329
306 /// Asserts the scope has a parent which is a ZIRModule and330 /// Asserts the scope has a parent which is a ZIRModule or File and
307 /// returns it.331 /// returns it.
308 pub fn namespace(self: *Scope) *ZIRModule {332 pub fn namespace(self: *Scope) *Scope {
309 switch (self.tag) {333 switch (self.tag) {
310 .block => return self.cast(Block).?.decl.scope,334 .block => return self.cast(Block).?.decl.scope,
335 .gen_zir => return self.cast(GenZIR).?.decl.scope,
336 .local_var => return self.cast(LocalVar).?.gen_zir.decl.scope,
311 .decl => return self.cast(DeclAnalysis).?.decl.scope,337 .decl => return self.cast(DeclAnalysis).?.decl.scope,
312 .zir_module => return self.cast(ZIRModule).?,338 .zir_module, .file => return self,
339 }
340 }
341
342 /// Must generate unique bytes with no collisions with other decls.
343 /// The point of hashing here is only to limit the number of bytes of
344 /// the unique identifier to a fixed size (16 bytes).
345 pub fn fullyQualifiedNameHash(self: *Scope, name: []const u8) NameHash {
346 switch (self.tag) {
347 .block => unreachable,
348 .gen_zir => unreachable,
349 .local_var => unreachable,
350 .decl => unreachable,
351 .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
352 .file => return self.cast(File).?.fullyQualifiedNameHash(name),
313 }353 }
314 }354 }
315355
356 /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
357 pub fn tree(self: *Scope) *ast.Tree {
358 switch (self.tag) {
359 .file => return self.cast(File).?.contents.tree,
360 .zir_module => unreachable,
361 .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(File).?.contents.tree,
362 .block => return self.cast(Block).?.decl.scope.cast(File).?.contents.tree,
363 .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(File).?.contents.tree,
364 .local_var => return self.cast(LocalVar).?.gen_zir.decl.scope.cast(File).?.contents.tree,
365 }
366 }
367
368 /// Asserts the scope is a child of a `GenZIR` and returns it.
369 pub fn getGenZIR(self: *Scope) *GenZIR {
370 return switch (self.tag) {
371 .block => unreachable,
372 .gen_zir => self.cast(GenZIR).?,
373 .local_var => return self.cast(LocalVar).?.gen_zir,
374 .decl => unreachable,
375 .zir_module => unreachable,
376 .file => unreachable,
377 };
378 }
379
316 pub fn dumpInst(self: *Scope, inst: *Inst) void {380 pub fn dumpInst(self: *Scope, inst: *Inst) void {
317 const zir_module = self.namespace();381 const zir_module = self.namespace();
318 const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src);382 const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src);
...@@ -325,10 +389,179 @@ pub const Scope = struct {...@@ -325,10 +389,179 @@ pub const Scope = struct {
325 });389 });
326 }390 }
327391
392 /// Asserts the scope has a parent which is a ZIRModule or File and
393 /// returns the sub_file_path field.
394 pub fn subFilePath(base: *Scope) []const u8 {
395 switch (base.tag) {
396 .file => return @fieldParentPtr(File, "base", base).sub_file_path,
397 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
398 .block => unreachable,
399 .gen_zir => unreachable,
400 .local_var => unreachable,
401 .decl => unreachable,
402 }
403 }
404
405 pub fn unload(base: *Scope, gpa: *Allocator) void {
406 switch (base.tag) {
407 .file => return @fieldParentPtr(File, "base", base).unload(gpa),
408 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
409 .block => unreachable,
410 .gen_zir => unreachable,
411 .local_var => unreachable,
412 .decl => unreachable,
413 }
414 }
415
416 pub fn getSource(base: *Scope, module: *Module) ![:0]const u8 {
417 switch (base.tag) {
418 .file => return @fieldParentPtr(File, "base", base).getSource(module),
419 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
420 .gen_zir => unreachable,
421 .local_var => unreachable,
422 .block => unreachable,
423 .decl => unreachable,
424 }
425 }
426
427 /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
428 pub fn removeDecl(base: *Scope, child: *Decl) void {
429 switch (base.tag) {
430 .file => return @fieldParentPtr(File, "base", base).removeDecl(child),
431 .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
432 .block => unreachable,
433 .gen_zir => unreachable,
434 .local_var => unreachable,
435 .decl => unreachable,
436 }
437 }
438
439 /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it.
440 pub fn destroy(base: *Scope, gpa: *Allocator) void {
441 switch (base.tag) {
442 .file => {
443 const scope_file = @fieldParentPtr(File, "base", base);
444 scope_file.deinit(gpa);
445 gpa.destroy(scope_file);
446 },
447 .zir_module => {
448 const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base);
449 scope_zir_module.deinit(gpa);
450 gpa.destroy(scope_zir_module);
451 },
452 .block => unreachable,
453 .gen_zir => unreachable,
454 .local_var => unreachable,
455 .decl => unreachable,
456 }
457 }
458
459 fn name_hash_hash(x: NameHash) u32 {
460 return @truncate(u32, @bitCast(u128, x));
461 }
462
463 fn name_hash_eql(a: NameHash, b: NameHash) bool {
464 return @bitCast(u128, a) == @bitCast(u128, b);
465 }
466
328 pub const Tag = enum {467 pub const Tag = enum {
468 /// .zir source code.
329 zir_module,469 zir_module,
470 /// .zig source code.
471 file,
330 block,472 block,
331 decl,473 decl,
474 gen_zir,
475 local_var,
476 };
477
478 pub const File = struct {
479 pub const base_tag: Tag = .file;
480 base: Scope = Scope{ .tag = base_tag },
481
482 /// Relative to the owning package's root_src_dir.
483 /// Reference to external memory, not owned by File.
484 sub_file_path: []const u8,
485 source: union(enum) {
486 unloaded: void,
487 bytes: [:0]const u8,
488 },
489 contents: union {
490 not_available: void,
491 tree: *ast.Tree,
492 },
493 status: enum {
494 never_loaded,
495 unloaded_success,
496 unloaded_parse_failure,
497 loaded_success,
498 },
499
500 /// Direct children of the file.
501 decls: ArrayListUnmanaged(*Decl),
502
503 pub fn unload(self: *File, gpa: *Allocator) void {
504 switch (self.status) {
505 .never_loaded,
506 .unloaded_parse_failure,
507 .unloaded_success,
508 => {},
509
510 .loaded_success => {
511 self.contents.tree.deinit();
512 self.status = .unloaded_success;
513 },
514 }
515 switch (self.source) {
516 .bytes => |bytes| {
517 gpa.free(bytes);
518 self.source = .{ .unloaded = {} };
519 },
520 .unloaded => {},
521 }
522 }
523
524 pub fn deinit(self: *File, gpa: *Allocator) void {
525 self.decls.deinit(gpa);
526 self.unload(gpa);
527 self.* = undefined;
528 }
529
530 pub fn removeDecl(self: *File, child: *Decl) void {
531 for (self.decls.items) |item, i| {
532 if (item == child) {
533 _ = self.decls.swapRemove(i);
534 return;
535 }
536 }
537 }
538
539 pub fn dumpSrc(self: *File, src: usize) void {
540 const loc = std.zig.findLineColumn(self.source.bytes, src);
541 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
542 }
543
544 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
545 switch (self.source) {
546 .unloaded => {
547 const source = try module.root_pkg.root_src_dir.readFileAllocOptions(
548 module.gpa,
549 self.sub_file_path,
550 std.math.maxInt(u32),
551 1,
552 0,
553 );
554 self.source = .{ .bytes = source };
555 return source;
556 },
557 .bytes => |bytes| return bytes,
558 }
559 }
560
561 pub fn fullyQualifiedNameHash(self: *File, name: []const u8) NameHash {
562 // We don't have struct scopes yet so this is currently just a simple name hash.
563 return std.zig.hashSrc(name);
564 }
332 };565 };
333566
334 pub const ZIRModule = struct {567 pub const ZIRModule = struct {
...@@ -355,7 +588,12 @@ pub const Scope = struct {...@@ -355,7 +588,12 @@ pub const Scope = struct {
355 loaded_success,588 loaded_success,
356 },589 },
357590
358 pub fn unload(self: *ZIRModule, allocator: *Allocator) void {591 /// Even though .zir files only have 1 module, this set is still needed
592 /// because of anonymous Decls, which can exist in the global set, but
593 /// not this one.
594 decls: ArrayListUnmanaged(*Decl),
595
596 pub fn unload(self: *ZIRModule, gpa: *Allocator) void {
359 switch (self.status) {597 switch (self.status) {
360 .never_loaded,598 .never_loaded,
361 .unloaded_parse_failure,599 .unloaded_parse_failure,
...@@ -364,34 +602,68 @@ pub const Scope = struct {...@@ -364,34 +602,68 @@ pub const Scope = struct {
364 => {},602 => {},
365603
366 .loaded_success => {604 .loaded_success => {
367 self.contents.module.deinit(allocator);605 self.contents.module.deinit(gpa);
368 allocator.destroy(self.contents.module);606 gpa.destroy(self.contents.module);
607 self.contents = .{ .not_available = {} };
369 self.status = .unloaded_success;608 self.status = .unloaded_success;
370 },609 },
371 .loaded_sema_failure => {610 .loaded_sema_failure => {
372 self.contents.module.deinit(allocator);611 self.contents.module.deinit(gpa);
373 allocator.destroy(self.contents.module);612 gpa.destroy(self.contents.module);
613 self.contents = .{ .not_available = {} };
374 self.status = .unloaded_sema_failure;614 self.status = .unloaded_sema_failure;
375 },615 },
376 }616 }
377 switch (self.source) {617 switch (self.source) {
378 .bytes => |bytes| {618 .bytes => |bytes| {
379 allocator.free(bytes);619 gpa.free(bytes);
380 self.source = .{ .unloaded = {} };620 self.source = .{ .unloaded = {} };
381 },621 },
382 .unloaded => {},622 .unloaded => {},
383 }623 }
384 }624 }
385625
386 pub fn deinit(self: *ZIRModule, allocator: *Allocator) void {626 pub fn deinit(self: *ZIRModule, gpa: *Allocator) void {
387 self.unload(allocator);627 self.decls.deinit(gpa);
628 self.unload(gpa);
388 self.* = undefined;629 self.* = undefined;
389 }630 }
390631
632 pub fn removeDecl(self: *ZIRModule, child: *Decl) void {
633 for (self.decls.items) |item, i| {
634 if (item == child) {
635 _ = self.decls.swapRemove(i);
636 return;
637 }
638 }
639 }
640
391 pub fn dumpSrc(self: *ZIRModule, src: usize) void {641 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
392 const loc = std.zig.findLineColumn(self.source.bytes, src);642 const loc = std.zig.findLineColumn(self.source.bytes, src);
393 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });643 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
394 }644 }
645
646 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
647 switch (self.source) {
648 .unloaded => {
649 const source = try module.root_pkg.root_src_dir.readFileAllocOptions(
650 module.gpa,
651 self.sub_file_path,
652 std.math.maxInt(u32),
653 1,
654 0,
655 );
656 self.source = .{ .bytes = source };
657 return source;
658 },
659 .bytes => |bytes| return bytes,
660 }
661 }
662
663 pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash {
664 // ZIR modules only have 1 file with all decls global in the same namespace.
665 return std.zig.hashSrc(name);
666 }
395 };667 };
396668
397 /// This is a temporary structure, references to it are valid only669 /// This is a temporary structure, references to it are valid only
...@@ -399,11 +671,19 @@ pub const Scope = struct {...@@ -399,11 +671,19 @@ pub const Scope = struct {
399 pub const Block = struct {671 pub const Block = struct {
400 pub const base_tag: Tag = .block;672 pub const base_tag: Tag = .block;
401 base: Scope = Scope{ .tag = base_tag },673 base: Scope = Scope{ .tag = base_tag },
402 func: *Fn,674 parent: ?*Block,
675 func: ?*Fn,
403 decl: *Decl,676 decl: *Decl,
404 instructions: ArrayListUnmanaged(*Inst),677 instructions: ArrayListUnmanaged(*Inst),
405 /// Points to the arena allocator of DeclAnalysis678 /// Points to the arena allocator of DeclAnalysis
406 arena: *Allocator,679 arena: *Allocator,
680 label: ?Label = null,
681
682 pub const Label = struct {
683 zir_block: *zir.Inst.Block,
684 results: ArrayListUnmanaged(*Inst),
685 block_inst: *Inst.Block,
686 };
407 };687 };
408688
409 /// This is a temporary structure, references to it are valid only689 /// This is a temporary structure, references to it are valid only
...@@ -414,10 +694,31 @@ pub const Scope = struct {...@@ -414,10 +694,31 @@ pub const Scope = struct {
414 decl: *Decl,694 decl: *Decl,
415 arena: std.heap.ArenaAllocator,695 arena: std.heap.ArenaAllocator,
416 };696 };
417};
418697
419pub const Body = struct {698 /// This is a temporary structure, references to it are valid only
420 instructions: []*Inst,699 /// during semantic analysis of the decl.
700 pub const GenZIR = struct {
701 pub const base_tag: Tag = .gen_zir;
702 base: Scope = Scope{ .tag = base_tag },
703 /// Parents can be: `GenZIR`, `ZIRModule`, `File`
704 parent: *Scope,
705 decl: *Decl,
706 arena: *Allocator,
707 /// The first N instructions in a function body ZIR are arg instructions.
708 instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
709 };
710
711 /// This structure lives as long as the AST generation of the Block
712 /// node that contains the variable.
713 pub const LocalVar = struct {
714 pub const base_tag: Tag = .local_var;
715 base: Scope = Scope{ .tag = base_tag },
716 /// Parents can be: `LocalVar`, `GenZIR`.
717 parent: *Scope,
718 gen_zir: *GenZIR,
719 name: []const u8,
720 inst: *zir.Inst,
721 };
421};722};
422723
423pub const AllErrors = struct {724pub const AllErrors = struct {
...@@ -432,8 +733,8 @@ pub const AllErrors = struct {...@@ -432,8 +733,8 @@ pub const AllErrors = struct {
432 msg: []const u8,733 msg: []const u8,
433 };734 };
434735
435 pub fn deinit(self: *AllErrors, allocator: *Allocator) void {736 pub fn deinit(self: *AllErrors, gpa: *Allocator) void {
436 self.arena.promote(allocator).deinit();737 self.arena.promote(gpa).deinit();
437 }738 }
438739
439 fn add(740 fn add(
...@@ -463,147 +764,163 @@ pub const InitOptions = struct {...@@ -463,147 +764,163 @@ pub const InitOptions = struct {
463 link_mode: ?std.builtin.LinkMode = null,764 link_mode: ?std.builtin.LinkMode = null,
464 object_format: ?std.builtin.ObjectFormat = null,765 object_format: ?std.builtin.ObjectFormat = null,
465 optimize_mode: std.builtin.Mode = .Debug,766 optimize_mode: std.builtin.Mode = .Debug,
767 keep_source_files_loaded: bool = false,
466};768};
467769
468pub fn init(gpa: *Allocator, options: InitOptions) !Module {770pub fn init(gpa: *Allocator, options: InitOptions) !Module {
469 const root_scope = try gpa.create(Scope.ZIRModule);
470 errdefer gpa.destroy(root_scope);
471
472 root_scope.* = .{
473 .sub_file_path = options.root_pkg.root_src_path,
474 .source = .{ .unloaded = {} },
475 .contents = .{ .not_available = {} },
476 .status = .never_loaded,
477 };
478
479 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();771 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
480 var bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{772 const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
481 .target = options.target,773 .target = options.target,
482 .output_mode = options.output_mode,774 .output_mode = options.output_mode,
483 .link_mode = options.link_mode orelse .Static,775 .link_mode = options.link_mode orelse .Static,
484 .object_format = options.object_format orelse options.target.getObjectFormat(),776 .object_format = options.object_format orelse options.target.getObjectFormat(),
485 });777 });
486 errdefer bin_file.deinit();778 errdefer bin_file.destroy();
779
780 const root_scope = blk: {
781 if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {
782 const root_scope = try gpa.create(Scope.File);
783 root_scope.* = .{
784 .sub_file_path = options.root_pkg.root_src_path,
785 .source = .{ .unloaded = {} },
786 .contents = .{ .not_available = {} },
787 .status = .never_loaded,
788 .decls = .{},
789 };
790 break :blk &root_scope.base;
791 } else if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zir")) {
792 const root_scope = try gpa.create(Scope.ZIRModule);
793 root_scope.* = .{
794 .sub_file_path = options.root_pkg.root_src_path,
795 .source = .{ .unloaded = {} },
796 .contents = .{ .not_available = {} },
797 .status = .never_loaded,
798 .decls = .{},
799 };
800 break :blk &root_scope.base;
801 } else {
802 unreachable;
803 }
804 };
487805
488 return Module{806 return Module{
489 .allocator = gpa,807 .gpa = gpa,
490 .root_pkg = options.root_pkg,808 .root_pkg = options.root_pkg,
491 .root_scope = root_scope,809 .root_scope = root_scope,
492 .bin_file_dir = bin_file_dir,810 .bin_file_dir = bin_file_dir,
493 .bin_file_path = options.bin_file_path,811 .bin_file_path = options.bin_file_path,
494 .bin_file = bin_file,812 .bin_file = bin_file,
495 .optimize_mode = options.optimize_mode,813 .optimize_mode = options.optimize_mode,
496 .decl_table = std.AutoHashMap(Decl.Hash, *Decl).init(gpa),
497 .decl_exports = std.AutoHashMap(*Decl, []*Export).init(gpa),
498 .export_owners = std.AutoHashMap(*Decl, []*Export).init(gpa),
499 .failed_decls = std.AutoHashMap(*Decl, *ErrorMsg).init(gpa),
500 .failed_files = std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg).init(gpa),
501 .failed_exports = std.AutoHashMap(*Export, *ErrorMsg).init(gpa),
502 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),814 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
815 .keep_source_files_loaded = options.keep_source_files_loaded,
503 };816 };
504}817}
505818
506pub fn deinit(self: *Module) void {819pub fn deinit(self: *Module) void {
507 self.bin_file.deinit();820 self.bin_file.destroy();
508 const allocator = self.allocator;821 const gpa = self.gpa;
509 self.deletion_set.deinit(allocator);822 self.deletion_set.deinit(gpa);
510 self.work_queue.deinit();823 self.work_queue.deinit();
511 {824
512 var it = self.decl_table.iterator();825 for (self.decl_table.items()) |entry| {
513 while (it.next()) |kv| {826 entry.value.destroy(gpa);
514 kv.value.destroy(allocator);
515 }
516 self.decl_table.deinit();
517 }
518 {
519 var it = self.failed_decls.iterator();
520 while (it.next()) |kv| {
521 kv.value.destroy(allocator);
522 }
523 self.failed_decls.deinit();
524 }827 }
525 {828 self.decl_table.deinit(gpa);
526 var it = self.failed_files.iterator();829
527 while (it.next()) |kv| {830 for (self.failed_decls.items()) |entry| {
528 kv.value.destroy(allocator);831 entry.value.destroy(gpa);
529 }
530 self.failed_files.deinit();
531 }832 }
532 {833 self.failed_decls.deinit(gpa);
533 var it = self.failed_exports.iterator();834
534 while (it.next()) |kv| {835 for (self.failed_files.items()) |entry| {
535 kv.value.destroy(allocator);836 entry.value.destroy(gpa);
536 }
537 self.failed_exports.deinit();
538 }837 }
539 {838 self.failed_files.deinit(gpa);
540 var it = self.decl_exports.iterator();839
541 while (it.next()) |kv| {840 for (self.failed_exports.items()) |entry| {
542 const export_list = kv.value;841 entry.value.destroy(gpa);
543 allocator.free(export_list);
544 }
545 self.decl_exports.deinit();
546 }842 }
547 {843 self.failed_exports.deinit(gpa);
548 var it = self.export_owners.iterator();844
549 while (it.next()) |kv| {845 for (self.decl_exports.items()) |entry| {
550 freeExportList(allocator, kv.value);846 const export_list = entry.value;
551 }847 gpa.free(export_list);
552 self.export_owners.deinit();
553 }848 }
554 {849 self.decl_exports.deinit(gpa);
555 self.root_scope.deinit(allocator);850
556 allocator.destroy(self.root_scope);851 for (self.export_owners.items()) |entry| {
852 freeExportList(gpa, entry.value);
557 }853 }
854 self.export_owners.deinit(gpa);
855
856 self.symbol_exports.deinit(gpa);
857 self.root_scope.destroy(gpa);
558 self.* = undefined;858 self.* = undefined;
559}859}
560860
561fn freeExportList(allocator: *Allocator, export_list: []*Export) void {861fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
562 for (export_list) |exp| {862 for (export_list) |exp| {
563 allocator.destroy(exp);863 gpa.destroy(exp);
564 }864 }
565 allocator.free(export_list);865 gpa.free(export_list);
566}866}
567867
568pub fn target(self: Module) std.Target {868pub fn target(self: Module) std.Target {
569 return self.bin_file.options.target;869 return self.bin_file.options().target;
570}870}
571871
572/// Detect changes to source files, perform semantic analysis, and update the output files.872/// Detect changes to source files, perform semantic analysis, and update the output files.
573pub fn update(self: *Module) !void {873pub fn update(self: *Module) !void {
874 const tracy = trace(@src());
875 defer tracy.end();
876
574 self.generation += 1;877 self.generation += 1;
575878
576 // TODO Use the cache hash file system to detect which source files changed.879 // TODO Use the cache hash file system to detect which source files changed.
577 // Here we simulate a full cache miss.880 // Until then we simulate a full cache miss. Source files could have been loaded for any reason;
578 // Analyze the root source file now.881 // to force a refresh we unload now.
579 // Source files could have been loaded for any reason; to force a refresh we unload now.882 if (self.root_scope.cast(Scope.File)) |zig_file| {
580 self.root_scope.unload(self.allocator);883 zig_file.unload(self.gpa);
581 self.analyzeRoot(self.root_scope) catch |err| switch (err) {884 self.analyzeRootSrcFile(zig_file) catch |err| switch (err) {
582 error.AnalysisFail => {885 error.AnalysisFail => {
583 assert(self.totalErrorCount() != 0);886 assert(self.totalErrorCount() != 0);
584 },887 },
585 else => |e| return e,888 else => |e| return e,
586 };889 };
890 } else if (self.root_scope.cast(Scope.ZIRModule)) |zir_module| {
891 zir_module.unload(self.gpa);
892 self.analyzeRootZIRModule(zir_module) catch |err| switch (err) {
893 error.AnalysisFail => {
894 assert(self.totalErrorCount() != 0);
895 },
896 else => |e| return e,
897 };
898 }
587899
588 try self.performAllTheWork();900 try self.performAllTheWork();
589901
590 // Process the deletion set.902 // Process the deletion set.
591 while (self.deletion_set.popOrNull()) |decl| {903 while (self.deletion_set.popOrNull()) |decl| {
592 if (decl.dependants.items.len != 0) {904 if (decl.dependants.items().len != 0) {
593 decl.deletion_flag = false;905 decl.deletion_flag = false;
594 continue;906 continue;
595 }907 }
596 try self.deleteDecl(decl);908 try self.deleteDecl(decl);
597 }909 }
598910
599 // If there are any errors, we anticipate the source files being loaded
600 // to report error messages. Otherwise we unload all source files to save memory.
601 if (self.totalErrorCount() == 0) {911 if (self.totalErrorCount() == 0) {
602 self.root_scope.unload(self.allocator);912 // This is needed before reading the error flags.
913 try self.bin_file.flush();
603 }914 }
604915
605 try self.bin_file.flush();916 self.link_error_flags = self.bin_file.errorFlags();
606 self.link_error_flags = self.bin_file.error_flags;917 std.log.debug(.module, "link_error_flags: {}\n", .{self.link_error_flags});
918
919 // If there are any errors, we anticipate the source files being loaded
920 // to report error messages. Otherwise we unload all source files to save memory.
921 if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) {
922 self.root_scope.unload(self.gpa);
923 }
607}924}
608925
609/// Having the file open for writing is problematic as far as executing the926/// Having the file open for writing is problematic as far as executing the
...@@ -619,48 +936,39 @@ pub fn makeBinFileWritable(self: *Module) !void {...@@ -619,48 +936,39 @@ pub fn makeBinFileWritable(self: *Module) !void {
619}936}
620937
621pub fn totalErrorCount(self: *Module) usize {938pub fn totalErrorCount(self: *Module) usize {
622 return self.failed_decls.size +939 const total = self.failed_decls.items().len +
623 self.failed_files.size +940 self.failed_files.items().len +
624 self.failed_exports.size +941 self.failed_exports.items().len;
625 @boolToInt(self.link_error_flags.no_entry_point_found);942 return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total;
626}943}
627944
628pub fn getAllErrorsAlloc(self: *Module) !AllErrors {945pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
629 var arena = std.heap.ArenaAllocator.init(self.allocator);946 var arena = std.heap.ArenaAllocator.init(self.gpa);
630 errdefer arena.deinit();947 errdefer arena.deinit();
631948
632 var errors = std.ArrayList(AllErrors.Message).init(self.allocator);949 var errors = std.ArrayList(AllErrors.Message).init(self.gpa);
633 defer errors.deinit();950 defer errors.deinit();
634951
635 {952 for (self.failed_files.items()) |entry| {
636 var it = self.failed_files.iterator();953 const scope = entry.key;
637 while (it.next()) |kv| {954 const err_msg = entry.value;
638 const scope = kv.key;955 const source = try scope.getSource(self);
639 const err_msg = kv.value;956 try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*);
640 const source = try self.getSource(scope);
641 try AllErrors.add(&arena, &errors, scope.sub_file_path, source, err_msg.*);
642 }
643 }957 }
644 {958 for (self.failed_decls.items()) |entry| {
645 var it = self.failed_decls.iterator();959 const decl = entry.key;
646 while (it.next()) |kv| {960 const err_msg = entry.value;
647 const decl = kv.key;961 const source = try decl.scope.getSource(self);
648 const err_msg = kv.value;962 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
649 const source = try self.getSource(decl.scope);
650 try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*);
651 }
652 }963 }
653 {964 for (self.failed_exports.items()) |entry| {
654 var it = self.failed_exports.iterator();965 const decl = entry.key.owner_decl;
655 while (it.next()) |kv| {966 const err_msg = entry.value;
656 const decl = kv.key.owner_decl;967 const source = try decl.scope.getSource(self);
657 const err_msg = kv.value;968 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
658 const source = try self.getSource(decl.scope);
659 try AllErrors.add(&arena, &errors, decl.scope.sub_file_path, source, err_msg.*);
660 }
661 }969 }
662970
663 if (self.link_error_flags.no_entry_point_found) {971 if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) {
664 try errors.append(.{972 try errors.append(.{
665 .src_path = self.root_pkg.root_src_path,973 .src_path = self.root_pkg.root_src_path,
666 .line = 0,974 .line = 0,
...@@ -678,17 +986,17 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {...@@ -678,17 +986,17 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
678 };986 };
679}987}
680988
681const InnerError = error{ OutOfMemory, AnalysisFail };
682
683pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {989pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
684 while (self.work_queue.readItem()) |work_item| switch (work_item) {990 while (self.work_queue.readItem()) |work_item| switch (work_item) {
685 .codegen_decl => |decl| switch (decl.analysis) {991 .codegen_decl => |decl| switch (decl.analysis) {
992 .unreferenced => unreachable,
686 .in_progress => unreachable,993 .in_progress => unreachable,
687 .outdated => unreachable,994 .outdated => unreachable,
688995
689 .sema_failure,996 .sema_failure,
690 .codegen_failure,997 .codegen_failure,
691 .dependency_failure,998 .dependency_failure,
999 .sema_failure_retryable,
692 => continue,1000 => continue,
6931001
694 .complete, .codegen_failure_retryable => {1002 .complete, .codegen_failure_retryable => {
...@@ -696,17 +1004,21 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -696,17 +1004,21 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
696 switch (payload.func.analysis) {1004 switch (payload.func.analysis) {
697 .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) {1005 .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) {
698 error.AnalysisFail => {1006 error.AnalysisFail => {
699 if (payload.func.analysis == .queued) {1007 assert(payload.func.analysis != .in_progress);
700 payload.func.analysis = .dependency_failure;
701 }
702 continue;1008 continue;
703 },1009 },
704 else => |e| return e,1010 error.OutOfMemory => return error.OutOfMemory,
705 },1011 },
706 .in_progress => unreachable,1012 .in_progress => unreachable,
707 .sema_failure, .dependency_failure => continue,1013 .sema_failure, .dependency_failure => continue,
708 .success => {},1014 .success => {},
709 }1015 }
1016 // Here we tack on additional allocations to the Decl's arena. The allocations are
1017 // lifetime annotations in the ZIR.
1018 var decl_arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
1019 defer decl.typed_value.most_recent.arena.?.* = decl_arena.state;
1020 std.log.debug(.module, "analyze liveness of {}\n", .{decl.name});
1021 try liveness.analyze(self.gpa, &decl_arena.allocator, payload.func.analysis.success);
710 }1022 }
7111023
712 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());1024 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
...@@ -716,108 +1028,363 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -716,108 +1028,363 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
716 error.AnalysisFail => {1028 error.AnalysisFail => {
717 decl.analysis = .dependency_failure;1029 decl.analysis = .dependency_failure;
718 },1030 },
1031 error.CGenFailure => {
1032 // Error is handled by CBE, don't try adding it again
1033 },
719 else => {1034 else => {
720 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);1035 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
721 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(1036 const result = self.failed_decls.getOrPutAssumeCapacity(decl);
722 self.allocator,1037 if (result.found_existing) {
723 decl.src,1038 std.debug.panic("Internal error: attempted to override error '{}' with 'unable to codegen: {}'", .{ result.entry.value.msg, @errorName(err) });
724 "unable to codegen: {}",1039 } else {
725 .{@errorName(err)},1040 result.entry.value = try ErrorMsg.create(
726 ));1041 self.gpa,
1042 decl.src(),
1043 "unable to codegen: {}",
1044 .{@errorName(err)},
1045 );
1046 }
727 decl.analysis = .codegen_failure_retryable;1047 decl.analysis = .codegen_failure_retryable;
728 },1048 },
729 };1049 };
730 },1050 },
731 },1051 },
732 .re_analyze_decl => |decl| switch (decl.analysis) {1052 .analyze_decl => |decl| {
733 .in_progress => unreachable,1053 self.ensureDeclAnalyzed(decl) catch |err| switch (err) {
1054 error.OutOfMemory => return error.OutOfMemory,
1055 error.AnalysisFail => continue,
1056 };
1057 },
1058 };
1059}
7341060
735 .sema_failure,1061fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
736 .codegen_failure,1062 const tracy = trace(@src());
737 .dependency_failure,1063 defer tracy.end();
738 .complete,
739 .codegen_failure_retryable,
740 => continue,
7411064
742 .outdated => {1065 const subsequent_analysis = switch (decl.analysis) {
743 const zir_module = self.getSrcModule(decl.scope) catch |err| switch (err) {1066 .in_progress => unreachable,
744 error.OutOfMemory => return error.OutOfMemory,1067
745 else => {1068 .sema_failure,
746 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);1069 .sema_failure_retryable,
747 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(1070 .codegen_failure,
748 self.allocator,1071 .dependency_failure,
749 decl.src,1072 .codegen_failure_retryable,
750 "unable to load source file '{}': {}",1073 => return error.AnalysisFail,
751 .{ decl.scope.sub_file_path, @errorName(err) },1074
752 ));1075 .complete, .outdated => blk: {
753 decl.analysis = .codegen_failure_retryable;1076 if (decl.generation == self.generation) {
754 continue;1077 assert(decl.analysis == .complete);
755 },1078 return;
756 };1079 }
757 const decl_name = mem.spanZ(decl.name);1080 //std.debug.warn("re-analyzing {}\n", .{decl.name});
758 // We already detected deletions, so we know this will be found.1081
759 const src_decl = zir_module.findDecl(decl_name).?;1082 // The exports this Decl performs will be re-discovered, so we remove them here
760 self.reAnalyzeDecl(decl, src_decl) catch |err| switch (err) {1083 // prior to re-analysis.
761 error.OutOfMemory => return error.OutOfMemory,1084 self.deleteDeclExports(decl);
762 error.AnalysisFail => continue,1085 // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
763 };1086 for (decl.dependencies.items()) |entry| {
764 },1087 const dep = entry.key;
1088 dep.removeDependant(decl);
1089 if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
1090 // We don't perform a deletion here, because this Decl or another one
1091 // may end up referencing it before the update is complete.
1092 dep.deletion_flag = true;
1093 try self.deletion_set.append(self.gpa, dep);
1094 }
1095 }
1096 decl.dependencies.clearRetainingCapacity();
1097
1098 break :blk true;
765 },1099 },
1100
1101 .unreferenced => false,
766 };1102 };
767}
7681103
769fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {1104 const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module|
770 try depender.dependencies.ensureCapacity(self.allocator, depender.dependencies.items.len + 1);1105 try self.analyzeZirDecl(decl, zir_module.contents.module.decls[decl.src_index])
771 try dependee.dependants.ensureCapacity(self.allocator, dependee.dependants.items.len + 1);1106 else
1107 self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
1108 error.OutOfMemory => return error.OutOfMemory,
1109 error.AnalysisFail => return error.AnalysisFail,
1110 else => {
1111 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
1112 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1113 self.gpa,
1114 decl.src(),
1115 "unable to analyze: {}",
1116 .{@errorName(err)},
1117 ));
1118 decl.analysis = .sema_failure_retryable;
1119 return error.AnalysisFail;
1120 },
1121 };
7721122
773 for (depender.dependencies.items) |item| {1123 if (subsequent_analysis) {
774 if (item == dependee) break; // Already in the set.1124 // We may need to chase the dependants and re-analyze them.
775 } else {1125 // However, if the decl is a function, and the type is the same, we do not need to.
776 depender.dependencies.appendAssumeCapacity(dependee);1126 if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) {
1127 for (decl.dependants.items()) |entry| {
1128 const dep = entry.key;
1129 switch (dep.analysis) {
1130 .unreferenced => unreachable,
1131 .in_progress => unreachable,
1132 .outdated => continue, // already queued for update
1133
1134 .dependency_failure,
1135 .sema_failure,
1136 .sema_failure_retryable,
1137 .codegen_failure,
1138 .codegen_failure_retryable,
1139 .complete,
1140 => if (dep.generation != self.generation) {
1141 try self.markOutdatedDecl(dep);
1142 },
1143 }
1144 }
1145 }
777 }1146 }
1147}
7781148
779 for (dependee.dependants.items) |item| {1149fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
780 if (item == depender) break; // Already in the set.1150 const tracy = trace(@src());
781 } else {1151 defer tracy.end();
782 dependee.dependants.appendAssumeCapacity(depender);1152
1153 const file_scope = decl.scope.cast(Scope.File).?;
1154 const tree = try self.getAstTree(file_scope);
1155 const ast_node = tree.root_node.decls()[decl.src_index];
1156 switch (ast_node.tag) {
1157 .FnProto => {
1158 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
1159
1160 decl.analysis = .in_progress;
1161
1162 // This arena allocator's memory is discarded at the end of this function. It is used
1163 // to determine the type of the function, and hence the type of the decl, which is needed
1164 // to complete the Decl analysis.
1165 var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1166 defer fn_type_scope_arena.deinit();
1167 var fn_type_scope: Scope.GenZIR = .{
1168 .decl = decl,
1169 .arena = &fn_type_scope_arena.allocator,
1170 .parent = decl.scope,
1171 };
1172 defer fn_type_scope.instructions.deinit(self.gpa);
1173
1174 const body_node = fn_proto.getTrailer("body_node") orelse
1175 return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
1176
1177 const param_decls = fn_proto.params();
1178 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
1179 for (param_decls) |param_decl, i| {
1180 const param_type_node = switch (param_decl.param_type) {
1181 .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
1182 .type_expr => |node| node,
1183 };
1184 param_types[i] = try astgen.expr(self, &fn_type_scope.base, param_type_node);
1185 }
1186 if (fn_proto.getTrailer("var_args_token")) |var_args_token| {
1187 return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
1188 }
1189 if (fn_proto.getTrailer("lib_name")) |lib_name| {
1190 return self.failNode(&fn_type_scope.base, lib_name, "TODO implement function library name", .{});
1191 }
1192 if (fn_proto.getTrailer("align_expr")) |align_expr| {
1193 return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{});
1194 }
1195 if (fn_proto.getTrailer("section_expr")) |sect_expr| {
1196 return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{});
1197 }
1198 if (fn_proto.getTrailer("callconv_expr")) |callconv_expr| {
1199 return self.failNode(
1200 &fn_type_scope.base,
1201 callconv_expr,
1202 "TODO implement function calling convention expression",
1203 .{},
1204 );
1205 }
1206 const return_type_expr = switch (fn_proto.return_type) {
1207 .Explicit => |node| node,
1208 .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}),
1209 .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
1210 };
1211
1212 const return_type_inst = try astgen.expr(self, &fn_type_scope.base, return_type_expr);
1213 const fn_src = tree.token_locs[fn_proto.fn_token].start;
1214 const fn_type_inst = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.FnType, .{
1215 .return_type = return_type_inst,
1216 .param_types = param_types,
1217 }, .{});
1218 _ = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.Return, .{ .operand = fn_type_inst }, .{});
1219
1220 // We need the memory for the Type to go into the arena for the Decl
1221 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
1222 errdefer decl_arena.deinit();
1223 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
1224
1225 var block_scope: Scope.Block = .{
1226 .parent = null,
1227 .func = null,
1228 .decl = decl,
1229 .instructions = .{},
1230 .arena = &decl_arena.allocator,
1231 };
1232 defer block_scope.instructions.deinit(self.gpa);
1233
1234 const fn_type = try self.analyzeBodyValueAsType(&block_scope, .{
1235 .instructions = fn_type_scope.instructions.items,
1236 });
1237 const new_func = try decl_arena.allocator.create(Fn);
1238 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
1239
1240 const fn_zir = blk: {
1241 // This scope's arena memory is discarded after the ZIR generation
1242 // pass completes, and semantic analysis of it completes.
1243 var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa);
1244 errdefer gen_scope_arena.deinit();
1245 var gen_scope: Scope.GenZIR = .{
1246 .decl = decl,
1247 .arena = &gen_scope_arena.allocator,
1248 .parent = decl.scope,
1249 };
1250 defer gen_scope.instructions.deinit(self.gpa);
1251
1252 // We need an instruction for each parameter, and they must be first in the body.
1253 try gen_scope.instructions.resize(self.gpa, fn_proto.params_len);
1254 var params_scope = &gen_scope.base;
1255 for (fn_proto.params()) |param, i| {
1256 const name_token = param.name_token.?;
1257 const src = tree.token_locs[name_token].start;
1258 const param_name = tree.tokenSlice(name_token);
1259 const arg = try newZIRInst(&gen_scope_arena.allocator, src, zir.Inst.Arg, .{}, .{});
1260 gen_scope.instructions.items[i] = &arg.base;
1261 const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVar);
1262 sub_scope.* = .{
1263 .parent = params_scope,
1264 .gen_zir = &gen_scope,
1265 .name = param_name,
1266 .inst = &arg.base,
1267 };
1268 params_scope = &sub_scope.base;
1269 }
1270
1271 const body_block = body_node.cast(ast.Node.Block).?;
1272
1273 try astgen.blockExpr(self, params_scope, body_block);
1274
1275 if (!fn_type.fnReturnType().isNoReturn() and (gen_scope.instructions.items.len == 0 or
1276 !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))
1277 {
1278 const src = tree.token_locs[body_block.rbrace].start;
1279 _ = try self.addZIRInst(&gen_scope.base, src, zir.Inst.ReturnVoid, .{}, .{});
1280 }
1281
1282 const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
1283 fn_zir.* = .{
1284 .body = .{
1285 .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items),
1286 },
1287 .arena = gen_scope_arena.state,
1288 };
1289 break :blk fn_zir;
1290 };
1291
1292 new_func.* = .{
1293 .analysis = .{ .queued = fn_zir },
1294 .owner_decl = decl,
1295 };
1296 fn_payload.* = .{ .func = new_func };
1297
1298 var prev_type_has_bits = false;
1299 var type_changed = true;
1300
1301 if (decl.typedValueManaged()) |tvm| {
1302 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1303 type_changed = !tvm.typed_value.ty.eql(fn_type);
1304
1305 tvm.deinit(self.gpa);
1306 }
1307
1308 decl_arena_state.* = decl_arena.state;
1309 decl.typed_value = .{
1310 .most_recent = .{
1311 .typed_value = .{
1312 .ty = fn_type,
1313 .val = Value.initPayload(&fn_payload.base),
1314 },
1315 .arena = decl_arena_state,
1316 },
1317 };
1318 decl.analysis = .complete;
1319 decl.generation = self.generation;
1320
1321 if (fn_type.hasCodeGenBits()) {
1322 // We don't fully codegen the decl until later, but we do need to reserve a global
1323 // offset table index for it. This allows us to codegen decls out of dependency order,
1324 // increasing how many computations can be done in parallel.
1325 try self.bin_file.allocateDeclIndexes(decl);
1326 try self.work_queue.writeItem(.{ .codegen_decl = decl });
1327 } else if (prev_type_has_bits) {
1328 self.bin_file.freeDecl(decl);
1329 }
1330
1331 if (fn_proto.getTrailer("extern_export_inline_token")) |maybe_export_token| {
1332 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
1333 const export_src = tree.token_locs[maybe_export_token].start;
1334 const name_loc = tree.token_locs[fn_proto.getTrailer("name_token").?];
1335 const name = tree.tokenSliceLoc(name_loc);
1336 // The scope needs to have the decl in it.
1337 try self.analyzeExport(&block_scope.base, export_src, name, decl);
1338 }
1339 }
1340 return type_changed;
1341 },
1342 .VarDecl => @panic("TODO var decl"),
1343 .Comptime => @panic("TODO comptime decl"),
1344 .Use => @panic("TODO usingnamespace decl"),
1345 else => unreachable,
783 }1346 }
784}1347}
7851348
786fn getSource(self: *Module, root_scope: *Scope.ZIRModule) ![:0]const u8 {1349fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Module.Body) !Type {
787 switch (root_scope.source) {1350 try self.analyzeBody(&block_scope.base, body);
788 .unloaded => {1351 for (block_scope.instructions.items) |inst| {
789 const source = try self.root_pkg.root_src_dir.readFileAllocOptions(1352 if (inst.cast(Inst.Ret)) |ret| {
790 self.allocator,1353 const val = try self.resolveConstValue(&block_scope.base, ret.args.operand);
791 root_scope.sub_file_path,1354 return val.toType();
792 std.math.maxInt(u32),1355 } else {
793 1,1356 return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{});
794 0,1357 }
795 );
796 root_scope.source = .{ .bytes = source };
797 return source;
798 },
799 .bytes => |bytes| return bytes,
800 }1358 }
1359 unreachable;
1360}
1361
1362fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
1363 try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
1364 try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
1365
1366 depender.dependencies.putAssumeCapacity(dependee, {});
1367 dependee.dependants.putAssumeCapacity(depender, {});
801}1368}
8021369
803fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {1370fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
804 switch (root_scope.status) {1371 switch (root_scope.status) {
805 .never_loaded, .unloaded_success => {1372 .never_loaded, .unloaded_success => {
806 try self.failed_files.ensureCapacity(self.failed_files.size + 1);1373 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
8071374
808 const source = try self.getSource(root_scope);1375 const source = try root_scope.getSource(self);
8091376
810 var keep_zir_module = false;1377 var keep_zir_module = false;
811 const zir_module = try self.allocator.create(zir.Module);1378 const zir_module = try self.gpa.create(zir.Module);
812 defer if (!keep_zir_module) self.allocator.destroy(zir_module);1379 defer if (!keep_zir_module) self.gpa.destroy(zir_module);
8131380
814 zir_module.* = try zir.parse(self.allocator, source);1381 zir_module.* = try zir.parse(self.gpa, source);
815 defer if (!keep_zir_module) zir_module.deinit(self.allocator);1382 defer if (!keep_zir_module) zir_module.deinit(self.gpa);
8161383
817 if (zir_module.error_msg) |src_err_msg| {1384 if (zir_module.error_msg) |src_err_msg| {
818 self.failed_files.putAssumeCapacityNoClobber(1385 self.failed_files.putAssumeCapacityNoClobber(
819 root_scope,1386 &root_scope.base,
820 try ErrorMsg.create(self.allocator, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),1387 try ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}),
821 );1388 );
822 root_scope.status = .unloaded_parse_failure;1389 root_scope.status = .unloaded_parse_failure;
823 return error.AnalysisFail;1390 return error.AnalysisFail;
...@@ -838,96 +1405,194 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {...@@ -838,96 +1405,194 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
838 }1405 }
839}1406}
8401407
841fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {1408fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1409 const tracy = trace(@src());
1410 defer tracy.end();
1411
842 switch (root_scope.status) {1412 switch (root_scope.status) {
843 .never_loaded => {1413 .never_loaded, .unloaded_success => {
844 const src_module = try self.getSrcModule(root_scope);1414 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
8451415
846 // Here we ensure enough queue capacity to store all the decls, so that later we can use1416 const source = try root_scope.getSource(self);
847 // appendAssumeCapacity.
848 try self.work_queue.ensureUnusedCapacity(src_module.decls.len);
8491417
850 for (src_module.decls) |decl| {1418 var keep_tree = false;
851 if (decl.cast(zir.Inst.Export)) |export_inst| {1419 const tree = try std.zig.parse(self.gpa, source);
852 _ = try self.resolveDecl(&root_scope.base, &export_inst.base);1420 defer if (!keep_tree) tree.deinit();
853 }1421
1422 if (tree.errors.len != 0) {
1423 const parse_err = tree.errors[0];
1424
1425 var msg = std.ArrayList(u8).init(self.gpa);
1426 defer msg.deinit();
1427
1428 try parse_err.render(tree.token_ids, msg.outStream());
1429 const err_msg = try self.gpa.create(ErrorMsg);
1430 err_msg.* = .{
1431 .msg = msg.toOwnedSlice(),
1432 .byte_offset = tree.token_locs[parse_err.loc()].start,
1433 };
1434
1435 self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
1436 root_scope.status = .unloaded_parse_failure;
1437 return error.AnalysisFail;
854 }1438 }
1439
1440 root_scope.status = .loaded_success;
1441 root_scope.contents = .{ .tree = tree };
1442 keep_tree = true;
1443
1444 return tree;
855 },1445 },
8561446
857 .unloaded_parse_failure,1447 .unloaded_parse_failure => return error.AnalysisFail,
858 .unloaded_sema_failure,
859 .unloaded_success,
860 .loaded_sema_failure,
861 .loaded_success,
862 => {
863 const src_module = try self.getSrcModule(root_scope);
864
865 var exports_to_resolve = std.ArrayList(*zir.Inst).init(self.allocator);
866 defer exports_to_resolve.deinit();
867
868 // Keep track of the decls that we expect to see in this file so that
869 // we know which ones have been deleted.
870 var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator);
871 defer deleted_decls.deinit();
872 try deleted_decls.ensureCapacity(self.decl_table.size);
873 {
874 var it = self.decl_table.iterator();
875 while (it.next()) |kv| {
876 deleted_decls.putAssumeCapacityNoClobber(kv.value, {});
877 }
878 }
8791448
880 for (src_module.decls) |src_decl| {1449 .loaded_success => return root_scope.contents.tree,
881 const name_hash = Decl.hashSimpleName(src_decl.name);1450 }
882 if (self.decl_table.get(name_hash)) |kv| {1451}
883 const decl = kv.value;1452
884 deleted_decls.removeAssertDiscard(decl);1453fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
885 const new_contents_hash = Decl.hashSimpleName(src_decl.contents);1454 // We may be analyzing it for the first time, or this may be
886 //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });1455 // an incremental update. This code handles both cases.
887 if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {1456 const tree = try self.getAstTree(root_scope);
888 //std.debug.warn("'{}' {x} => {x}\n", .{ src_decl.name, decl.contents_hash, new_contents_hash });1457 const decls = tree.root_node.decls();
1458
1459 try self.work_queue.ensureUnusedCapacity(decls.len);
1460 try root_scope.decls.ensureCapacity(self.gpa, decls.len);
1461
1462 // Keep track of the decls that we expect to see in this file so that
1463 // we know which ones have been deleted.
1464 var deleted_decls = std.AutoHashMap(*Decl, void).init(self.gpa);
1465 defer deleted_decls.deinit();
1466 try deleted_decls.ensureCapacity(root_scope.decls.items.len);
1467 for (root_scope.decls.items) |file_decl| {
1468 deleted_decls.putAssumeCapacityNoClobber(file_decl, {});
1469 }
1470
1471 for (decls) |src_decl, decl_i| {
1472 if (src_decl.cast(ast.Node.FnProto)) |fn_proto| {
1473 // We will create a Decl for it regardless of analysis status.
1474 const name_tok = fn_proto.getTrailer("name_token") orelse {
1475 @panic("TODO missing function name");
1476 };
1477
1478 const name_loc = tree.token_locs[name_tok];
1479 const name = tree.tokenSliceLoc(name_loc);
1480 const name_hash = root_scope.fullyQualifiedNameHash(name);
1481 const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl));
1482 if (self.decl_table.get(name_hash)) |decl| {
1483 // Update the AST Node index of the decl, even if its contents are unchanged, it may
1484 // have been re-ordered.
1485 decl.src_index = decl_i;
1486 if (deleted_decls.remove(decl) == null) {
1487 decl.analysis = .sema_failure;
1488 const err_msg = try ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name});
1489 errdefer err_msg.destroy(self.gpa);
1490 try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
1491 } else {
1492 if (!srcHashEql(decl.contents_hash, contents_hash)) {
889 try self.markOutdatedDecl(decl);1493 try self.markOutdatedDecl(decl);
890 decl.contents_hash = new_contents_hash;1494 decl.contents_hash = contents_hash;
891 }1495 }
892 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {
893 try exports_to_resolve.append(&export_inst.base);
894 }1496 }
895 }1497 } else {
896 {1498 const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash);
897 // Handle explicitly deleted decls from the source code. Not to be confused1499 root_scope.decls.appendAssumeCapacity(new_decl);
898 // with when we delete decls because they are no longer referenced.1500 if (fn_proto.getTrailer("extern_export_inline_token")) |maybe_export_token| {
899 var it = deleted_decls.iterator();1501 if (tree.token_ids[maybe_export_token] == .Keyword_export) {
900 while (it.next()) |kv| {1502 self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
901 //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name});1503 }
902 try self.deleteDecl(kv.key);
903 }1504 }
904 }1505 }
905 for (exports_to_resolve.items) |export_inst| {1506 }
906 _ = try self.resolveDecl(&root_scope.base, export_inst);1507 // TODO also look for global variable declarations
1508 // TODO also look for comptime blocks and exported globals
1509 }
1510 // Handle explicitly deleted decls from the source code. Not to be confused
1511 // with when we delete decls because they are no longer referenced.
1512 for (deleted_decls.items()) |entry| {
1513 //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name});
1514 try self.deleteDecl(entry.key);
1515 }
1516}
1517
1518fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
1519 // We may be analyzing it for the first time, or this may be
1520 // an incremental update. This code handles both cases.
1521 const src_module = try self.getSrcModule(root_scope);
1522
1523 try self.work_queue.ensureUnusedCapacity(src_module.decls.len);
1524 try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len);
1525
1526 var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa);
1527 defer exports_to_resolve.deinit();
1528
1529 // Keep track of the decls that we expect to see in this file so that
1530 // we know which ones have been deleted.
1531 var deleted_decls = std.AutoHashMap(*Decl, void).init(self.gpa);
1532 defer deleted_decls.deinit();
1533 try deleted_decls.ensureCapacity(self.decl_table.items().len);
1534 for (self.decl_table.items()) |entry| {
1535 deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
1536 }
1537
1538 for (src_module.decls) |src_decl, decl_i| {
1539 const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
1540 if (self.decl_table.get(name_hash)) |decl| {
1541 deleted_decls.removeAssertDiscard(decl);
1542 //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });
1543 if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
1544 try self.markOutdatedDecl(decl);
1545 decl.contents_hash = src_decl.contents_hash;
907 }1546 }
908 },1547 } else {
1548 const new_decl = try self.createNewDecl(
1549 &root_scope.base,
1550 src_decl.name,
1551 decl_i,
1552 name_hash,
1553 src_decl.contents_hash,
1554 );
1555 root_scope.decls.appendAssumeCapacity(new_decl);
1556 if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| {
1557 try exports_to_resolve.append(src_decl);
1558 }
1559 }
1560 }
1561 for (exports_to_resolve.items) |export_decl| {
1562 _ = try self.resolveZirDecl(&root_scope.base, export_decl);
1563 }
1564 // Handle explicitly deleted decls from the source code. Not to be confused
1565 // with when we delete decls because they are no longer referenced.
1566 for (deleted_decls.items()) |entry| {
1567 //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name});
1568 try self.deleteDecl(entry.key);
909 }1569 }
910}1570}
9111571
912fn deleteDecl(self: *Module, decl: *Decl) !void {1572fn deleteDecl(self: *Module, decl: *Decl) !void {
913 try self.deletion_set.ensureCapacity(self.allocator, self.deletion_set.items.len + decl.dependencies.items.len);1573 try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
1574
1575 // Remove from the namespace it resides in. In the case of an anonymous Decl it will
1576 // not be present in the set, and this does nothing.
1577 decl.scope.removeDecl(decl);
9141578
915 //std.debug.warn("deleting decl '{}'\n", .{decl.name});1579 //std.debug.warn("deleting decl '{}'\n", .{decl.name});
916 const name_hash = decl.fullyQualifiedNameHash();1580 const name_hash = decl.fullyQualifiedNameHash();
917 self.decl_table.removeAssertDiscard(name_hash);1581 self.decl_table.removeAssertDiscard(name_hash);
918 // Remove itself from its dependencies, because we are about to destroy the decl pointer.1582 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
919 for (decl.dependencies.items) |dep| {1583 for (decl.dependencies.items()) |entry| {
1584 const dep = entry.key;
920 dep.removeDependant(decl);1585 dep.removeDependant(decl);
921 if (dep.dependants.items.len == 0) {1586 if (dep.dependants.items().len == 0 and !dep.deletion_flag) {
922 // We don't recursively perform a deletion here, because during the update,1587 // We don't recursively perform a deletion here, because during the update,
923 // another reference to it may turn up.1588 // another reference to it may turn up.
924 assert(!dep.deletion_flag);
925 dep.deletion_flag = true;1589 dep.deletion_flag = true;
926 self.deletion_set.appendAssumeCapacity(dep);1590 self.deletion_set.appendAssumeCapacity(dep);
927 }1591 }
928 }1592 }
929 // Anything that depends on this deleted decl certainly needs to be re-analyzed.1593 // Anything that depends on this deleted decl certainly needs to be re-analyzed.
930 for (decl.dependants.items) |dep| {1594 for (decl.dependants.items()) |entry| {
1595 const dep = entry.key;
931 dep.removeDependency(decl);1596 dep.removeDependency(decl);
932 if (dep.analysis != .outdated) {1597 if (dep.analysis != .outdated) {
933 // TODO Move this failure possibility to the top of the function.1598 // TODO Move this failure possibility to the top of the function.
...@@ -935,11 +1600,11 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {...@@ -935,11 +1600,11 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
935 }1600 }
936 }1601 }
937 if (self.failed_decls.remove(decl)) |entry| {1602 if (self.failed_decls.remove(decl)) |entry| {
938 entry.value.destroy(self.allocator);1603 entry.value.destroy(self.gpa);
939 }1604 }
940 self.deleteDeclExports(decl);1605 self.deleteDeclExports(decl);
941 self.bin_file.freeDecl(decl);1606 self.bin_file.freeDecl(decl);
942 decl.destroy(self.allocator);1607 decl.destroy(self.gpa);
943}1608}
9441609
945/// Delete all the Export objects that are caused by this Decl. Re-analysis of1610/// Delete all the Export objects that are caused by this Decl. Re-analysis of
...@@ -948,7 +1613,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {...@@ -948,7 +1613,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
948 const kv = self.export_owners.remove(decl) orelse return;1613 const kv = self.export_owners.remove(decl) orelse return;
9491614
950 for (kv.value) |exp| {1615 for (kv.value) |exp| {
951 if (self.decl_exports.get(exp.exported_decl)) |decl_exports_kv| {1616 if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| {
952 // Remove exports with owner_decl matching the regenerating decl.1617 // Remove exports with owner_decl matching the regenerating decl.
953 const list = decl_exports_kv.value;1618 const list = decl_exports_kv.value;
954 var i: usize = 0;1619 var i: usize = 0;
...@@ -961,96 +1626,108 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {...@@ -961,96 +1626,108 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
961 i += 1;1626 i += 1;
962 }1627 }
963 }1628 }
964 decl_exports_kv.value = self.allocator.shrink(list, new_len);1629 decl_exports_kv.value = self.gpa.shrink(list, new_len);
965 if (new_len == 0) {1630 if (new_len == 0) {
966 self.decl_exports.removeAssertDiscard(exp.exported_decl);1631 self.decl_exports.removeAssertDiscard(exp.exported_decl);
967 }1632 }
968 }1633 }
9691634 if (self.bin_file.cast(link.File.Elf)) |elf| {
970 self.bin_file.deleteExport(exp.link);1635 elf.deleteExport(exp.link);
971 self.allocator.destroy(exp);1636 }
1637 if (self.failed_exports.remove(exp)) |entry| {
1638 entry.value.destroy(self.gpa);
1639 }
1640 _ = self.symbol_exports.remove(exp.options.name);
1641 self.gpa.destroy(exp);
972 }1642 }
973 self.allocator.free(kv.value);1643 self.gpa.free(kv.value);
974}1644}
9751645
976fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {1646fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1647 const tracy = trace(@src());
1648 defer tracy.end();
1649
977 // Use the Decl's arena for function memory.1650 // Use the Decl's arena for function memory.
978 var arena = decl.typed_value.most_recent.arena.?.promote(self.allocator);1651 var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa);
979 defer decl.typed_value.most_recent.arena.?.* = arena.state;1652 defer decl.typed_value.most_recent.arena.?.* = arena.state;
980 var analysis: Fn.Analysis = .{1653 var inner_block: Scope.Block = .{
981 .inner_block = .{1654 .parent = null,
982 .func = func,1655 .func = func,
983 .decl = decl,1656 .decl = decl,
984 .instructions = .{},1657 .instructions = .{},
985 .arena = &arena.allocator,1658 .arena = &arena.allocator,
986 },
987 .needed_inst_capacity = 0,
988 .inst_table = std.AutoHashMap(*zir.Inst, *Inst).init(self.allocator),
989 };1659 };
990 defer analysis.inner_block.instructions.deinit(self.allocator);1660 defer inner_block.instructions.deinit(self.gpa);
991 defer analysis.inst_table.deinit();
9921661
993 const fn_inst = func.analysis.queued;1662 const fn_zir = func.analysis.queued;
994 func.analysis = .{ .in_progress = &analysis };1663 defer fn_zir.arena.promote(self.gpa).deinit();
1664 func.analysis = .{ .in_progress = {} };
1665 //std.debug.warn("set {} to in_progress\n", .{decl.name});
9951666
996 try self.analyzeBody(&analysis.inner_block.base, fn_inst.positionals.body);1667 try self.analyzeBody(&inner_block.base, fn_zir.body);
9971668
998 func.analysis = .{1669 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
999 .success = .{1670 func.analysis = .{ .success = .{ .instructions = instructions } };
1000 .instructions = try arena.allocator.dupe(*Inst, analysis.inner_block.instructions.items),1671 //std.debug.warn("set {} to success\n", .{decl.name});
1001 },
1002 };
1003}1672}
10041673
1005fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!void {1674fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1006 switch (decl.analysis) {1675 //std.debug.warn("mark {} outdated\n", .{decl.name});
1007 .in_progress => unreachable,1676 try self.work_queue.writeItem(.{ .analyze_decl = decl });
1008 .dependency_failure,1677 if (self.failed_decls.remove(decl)) |entry| {
1009 .sema_failure,1678 entry.value.destroy(self.gpa);
1010 .codegen_failure,
1011 .codegen_failure_retryable,
1012 .complete,
1013 => return,
1014
1015 .outdated => {}, // Decl re-analysis
1016 }1679 }
1017 //std.debug.warn("re-analyzing {}\n", .{decl.name});1680 decl.analysis = .outdated;
1018 decl.src = old_inst.src;1681}
1682
1683fn allocateNewDecl(
1684 self: *Module,
1685 scope: *Scope,
1686 src_index: usize,
1687 contents_hash: std.zig.SrcHash,
1688) !*Decl {
1689 const new_decl = try self.gpa.create(Decl);
1690 new_decl.* = .{
1691 .name = "",
1692 .scope = scope.namespace(),
1693 .src_index = src_index,
1694 .typed_value = .{ .never_succeeded = {} },
1695 .analysis = .unreferenced,
1696 .deletion_flag = false,
1697 .contents_hash = contents_hash,
1698 .link = link.File.Elf.TextBlock.empty,
1699 .generation = 0,
1700 };
1701 return new_decl;
1702}
10191703
1020 // The exports this Decl performs will be re-discovered, so we remove them here1704fn createNewDecl(
1021 // prior to re-analysis.1705 self: *Module,
1022 self.deleteDeclExports(decl);1706 scope: *Scope,
1023 // Dependencies will be re-discovered, so we remove them here prior to re-analysis.1707 decl_name: []const u8,
1024 for (decl.dependencies.items) |dep| {1708 src_index: usize,
1025 dep.removeDependant(decl);1709 name_hash: Scope.NameHash,
1026 if (dep.dependants.items.len == 0) {1710 contents_hash: std.zig.SrcHash,
1027 // We don't perform a deletion here, because this Decl or another one1711) !*Decl {
1028 // may end up referencing it before the update is complete.1712 try self.decl_table.ensureCapacity(self.gpa, self.decl_table.items().len + 1);
1029 assert(!dep.deletion_flag);1713 const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash);
1030 dep.deletion_flag = true;1714 errdefer self.gpa.destroy(new_decl);
1031 try self.deletion_set.append(self.allocator, dep);1715 new_decl.name = try mem.dupeZ(self.gpa, u8, decl_name);
1032 }1716 self.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl);
1033 }1717 return new_decl;
1034 decl.dependencies.shrink(self.allocator, 0);1718}
1719
1720fn analyzeZirDecl(self: *Module, decl: *Decl, src_decl: *zir.Decl) InnerError!bool {
1035 var decl_scope: Scope.DeclAnalysis = .{1721 var decl_scope: Scope.DeclAnalysis = .{
1036 .decl = decl,1722 .decl = decl,
1037 .arena = std.heap.ArenaAllocator.init(self.allocator),1723 .arena = std.heap.ArenaAllocator.init(self.gpa),
1038 };1724 };
1039 errdefer decl_scope.arena.deinit();1725 errdefer decl_scope.arena.deinit();
10401726
1041 const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) {1727 decl.analysis = .in_progress;
1042 error.OutOfMemory => return error.OutOfMemory,1728
1043 error.AnalysisFail => {1729 const typed_value = try self.analyzeConstInst(&decl_scope.base, src_decl.inst);
1044 switch (decl.analysis) {
1045 .in_progress => decl.analysis = .dependency_failure,
1046 else => {},
1047 }
1048 decl.generation = self.generation;
1049 return error.AnalysisFail;
1050 },
1051 };
1052 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);1730 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
1053 arena_state.* = decl_scope.arena.state;
10541731
1055 var prev_type_has_bits = false;1732 var prev_type_has_bits = false;
1056 var type_changed = true;1733 var type_changed = true;
...@@ -1059,8 +1736,10 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi...@@ -1059,8 +1736,10 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi
1059 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();1736 prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits();
1060 type_changed = !tvm.typed_value.ty.eql(typed_value.ty);1737 type_changed = !tvm.typed_value.ty.eql(typed_value.ty);
10611738
1062 tvm.deinit(self.allocator);1739 tvm.deinit(self.gpa);
1063 }1740 }
1741
1742 arena_state.* = decl_scope.arena.state;
1064 decl.typed_value = .{1743 decl.typed_value = .{
1065 .most_recent = .{1744 .most_recent = .{
1066 .typed_value = typed_value,1745 .typed_value = typed_value,
...@@ -1079,137 +1758,66 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi...@@ -1079,137 +1758,66 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi
1079 self.bin_file.freeDecl(decl);1758 self.bin_file.freeDecl(decl);
1080 }1759 }
10811760
1082 // If the decl is a function, and the type is the same, we do not need1761 return type_changed;
1083 // to chase the dependants.
1084 if (type_changed or typed_value.val.tag() != .function) {
1085 for (decl.dependants.items) |dep| {
1086 switch (dep.analysis) {
1087 .in_progress => unreachable,
1088 .outdated => continue, // already queued for update
1089
1090 .dependency_failure,
1091 .sema_failure,
1092 .codegen_failure,
1093 .codegen_failure_retryable,
1094 .complete,
1095 => if (dep.generation != self.generation) {
1096 try self.markOutdatedDecl(dep);
1097 },
1098 }
1099 }
1100 }
1101}1762}
11021763
1103fn markOutdatedDecl(self: *Module, decl: *Decl) !void {1764fn resolveZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
1104 //std.debug.warn("mark {} outdated\n", .{decl.name});1765 const zir_module = self.root_scope.cast(Scope.ZIRModule).?;
1105 try self.work_queue.writeItem(.{ .re_analyze_decl = decl });1766 const entry = zir_module.contents.module.findDecl(src_decl.name).?;
1106 if (self.failed_decls.remove(decl)) |entry| {1767 return self.resolveZirDeclHavingIndex(scope, src_decl, entry.index);
1107 entry.value.destroy(self.allocator);
1108 }
1109 decl.analysis = .outdated;
1110}1768}
11111769
1112fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {1770fn resolveZirDeclHavingIndex(self: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl {
1113 const hash = Decl.hashSimpleName(old_inst.name);1771 const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name);
1114 if (self.decl_table.get(hash)) |kv| {1772 const decl = self.decl_table.get(name_hash).?;
1115 const decl = kv.value;1773 decl.src_index = src_index;
1116 try self.reAnalyzeDecl(decl, old_inst);1774 try self.ensureDeclAnalyzed(decl);
1117 return decl;1775 return decl;
1118 } else if (old_inst.cast(zir.Inst.DeclVal)) |decl_val| {
1119 // This is just a named reference to another decl.
1120 return self.analyzeDeclVal(scope, decl_val);
1121 } else {
1122 const new_decl = blk: {
1123 try self.decl_table.ensureCapacity(self.decl_table.size + 1);
1124 const new_decl = try self.allocator.create(Decl);
1125 errdefer self.allocator.destroy(new_decl);
1126 const name = try mem.dupeZ(self.allocator, u8, old_inst.name);
1127 errdefer self.allocator.free(name);
1128 new_decl.* = .{
1129 .name = name,
1130 .scope = scope.namespace(),
1131 .src = old_inst.src,
1132 .typed_value = .{ .never_succeeded = {} },
1133 .analysis = .in_progress,
1134 .deletion_flag = false,
1135 .contents_hash = Decl.hashSimpleName(old_inst.contents),
1136 .link = link.ElfFile.TextBlock.empty,
1137 .generation = 0,
1138 };
1139 self.decl_table.putAssumeCapacityNoClobber(hash, new_decl);
1140 break :blk new_decl;
1141 };
1142
1143 var decl_scope: Scope.DeclAnalysis = .{
1144 .decl = new_decl,
1145 .arena = std.heap.ArenaAllocator.init(self.allocator),
1146 };
1147 errdefer decl_scope.arena.deinit();
1148
1149 const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) {
1150 error.OutOfMemory => return error.OutOfMemory,
1151 error.AnalysisFail => {
1152 switch (new_decl.analysis) {
1153 .in_progress => new_decl.analysis = .dependency_failure,
1154 else => {},
1155 }
1156 new_decl.generation = self.generation;
1157 return error.AnalysisFail;
1158 },
1159 };
1160 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
1161
1162 arena_state.* = decl_scope.arena.state;
1163
1164 new_decl.typed_value = .{
1165 .most_recent = .{
1166 .typed_value = typed_value,
1167 .arena = arena_state,
1168 },
1169 };
1170 new_decl.analysis = .complete;
1171 new_decl.generation = self.generation;
1172 if (typed_value.ty.hasCodeGenBits()) {
1173 // We don't fully codegen the decl until later, but we do need to reserve a global
1174 // offset table index for it. This allows us to codegen decls out of dependency order,
1175 // increasing how many computations can be done in parallel.
1176 try self.bin_file.allocateDeclIndexes(new_decl);
1177 try self.work_queue.writeItem(.{ .codegen_decl = new_decl });
1178 }
1179 return new_decl;
1180 }
1181}1776}
11821777
1183/// Declares a dependency on the decl.1778/// Declares a dependency on the decl.
1184fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {1779fn resolveCompleteZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError!*Decl {
1185 const decl = try self.resolveDecl(scope, old_inst);1780 const decl = try self.resolveZirDecl(scope, src_decl);
1186 switch (decl.analysis) {1781 switch (decl.analysis) {
1782 .unreferenced => unreachable,
1187 .in_progress => unreachable,1783 .in_progress => unreachable,
1188 .outdated => unreachable,1784 .outdated => unreachable,
11891785
1190 .dependency_failure,1786 .dependency_failure,
1191 .sema_failure,1787 .sema_failure,
1788 .sema_failure_retryable,
1192 .codegen_failure,1789 .codegen_failure,
1193 .codegen_failure_retryable,1790 .codegen_failure_retryable,
1194 => return error.AnalysisFail,1791 => return error.AnalysisFail,
11951792
1196 .complete => {},1793 .complete => {},
1197 }1794 }
1198 if (scope.decl()) |scope_decl| {
1199 try self.declareDeclDependency(scope_decl, decl);
1200 }
1201 return decl;1795 return decl;
1202}1796}
12031797
1798/// TODO Look into removing this function. The body is only needed for .zir files, not .zig files.
1204fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {1799fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
1205 if (scope.cast(Scope.Block)) |block| {1800 if (old_inst.analyzed_inst) |inst| return inst;
1206 if (block.func.analysis.in_progress.inst_table.get(old_inst)) |kv| {1801
1207 return kv.value;1802 // If this assert trips, the instruction that was referenced did not get properly
1208 }1803 // analyzed before it was referenced.
1209 }1804 const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
12101805 const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: {
1211 const decl = try self.resolveCompleteDecl(scope, old_inst);1806 const decl_name = declval.positionals.name;
1807 const entry = zir_module.contents.module.findDecl(decl_name) orelse
1808 return self.fail(scope, old_inst.src, "decl '{}' not found", .{decl_name});
1809 break :blk entry;
1810 } else blk: {
1811 // If this assert trips, the instruction that was referenced did not get
1812 // properly analyzed by a previous instruction analysis before it was
1813 // referenced by the current one.
1814 break :blk zir_module.contents.module.findInstDecl(old_inst).?;
1815 };
1816 const decl = try self.resolveCompleteZirDecl(scope, entry.decl);
1212 const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);1817 const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
1818 // Note: it would be tempting here to store the result into old_inst.analyzed_inst field,
1819 // but this would prevent the analyzeDeclRef from happening, which is needed to properly
1820 // detect Decl dependencies and dependency failures on updates.
1213 return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);1821 return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
1214}1822}
12151823
...@@ -1258,29 +1866,25 @@ fn resolveType(self: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {...@@ -1258,29 +1866,25 @@ fn resolveType(self: *Module, scope: *Scope, old_inst: *zir.Inst) !Type {
1258 return val.toType();1866 return val.toType();
1259}1867}
12601868
1261fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!void {1869fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const u8, exported_decl: *Decl) !void {
1262 try self.decl_exports.ensureCapacity(self.decl_exports.size + 1);1870 try self.ensureDeclAnalyzed(exported_decl);
1263 try self.export_owners.ensureCapacity(self.export_owners.size + 1);
1264 const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
1265 const exported_decl = try self.resolveCompleteDecl(scope, export_inst.positionals.value);
1266 const typed_value = exported_decl.typed_value.most_recent.typed_value;1871 const typed_value = exported_decl.typed_value.most_recent.typed_value;
1267 switch (typed_value.ty.zigTypeTag()) {1872 switch (typed_value.ty.zigTypeTag()) {
1268 .Fn => {},1873 .Fn => {},
1269 else => return self.fail(1874 else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
1270 scope,
1271 export_inst.positionals.value.src,
1272 "unable to export type '{}'",
1273 .{typed_value.ty},
1274 ),
1275 }1875 }
1276 const new_export = try self.allocator.create(Export);1876
1277 errdefer self.allocator.destroy(new_export);1877 try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1);
1878 try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1);
1879
1880 const new_export = try self.gpa.create(Export);
1881 errdefer self.gpa.destroy(new_export);
12781882
1279 const owner_decl = scope.decl().?;1883 const owner_decl = scope.decl().?;
12801884
1281 new_export.* = .{1885 new_export.* = .{
1282 .options = .{ .name = symbol_name },1886 .options = .{ .name = symbol_name },
1283 .src = export_inst.base.src,1887 .src = src,
1284 .link = .{},1888 .link = .{},
1285 .owner_decl = owner_decl,1889 .owner_decl = owner_decl,
1286 .exported_decl = exported_decl,1890 .exported_decl = exported_decl,
...@@ -1288,30 +1892,44 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In...@@ -1288,30 +1892,44 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In
1288 };1892 };
12891893
1290 // Add to export_owners table.1894 // Add to export_owners table.
1291 const eo_gop = self.export_owners.getOrPut(owner_decl) catch unreachable;1895 const eo_gop = self.export_owners.getOrPut(self.gpa, owner_decl) catch unreachable;
1292 if (!eo_gop.found_existing) {1896 if (!eo_gop.found_existing) {
1293 eo_gop.kv.value = &[0]*Export{};1897 eo_gop.entry.value = &[0]*Export{};
1294 }1898 }
1295 eo_gop.kv.value = try self.allocator.realloc(eo_gop.kv.value, eo_gop.kv.value.len + 1);1899 eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1);
1296 eo_gop.kv.value[eo_gop.kv.value.len - 1] = new_export;1900 eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export;
1297 errdefer eo_gop.kv.value = self.allocator.shrink(eo_gop.kv.value, eo_gop.kv.value.len - 1);1901 errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
12981902
1299 // Add to exported_decl table.1903 // Add to exported_decl table.
1300 const de_gop = self.decl_exports.getOrPut(exported_decl) catch unreachable;1904 const de_gop = self.decl_exports.getOrPut(self.gpa, exported_decl) catch unreachable;
1301 if (!de_gop.found_existing) {1905 if (!de_gop.found_existing) {
1302 de_gop.kv.value = &[0]*Export{};1906 de_gop.entry.value = &[0]*Export{};
1303 }1907 }
1304 de_gop.kv.value = try self.allocator.realloc(de_gop.kv.value, de_gop.kv.value.len + 1);1908 de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1);
1305 de_gop.kv.value[de_gop.kv.value.len - 1] = new_export;1909 de_gop.entry.value[de_gop.entry.value.len - 1] = new_export;
1306 errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1);1910 errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1);
13071911
1308 self.bin_file.updateDeclExports(self, exported_decl, de_gop.kv.value) catch |err| switch (err) {1912 if (self.symbol_exports.get(symbol_name)) |_| {
1913 try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
1914 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
1915 self.gpa,
1916 src,
1917 "exported symbol collision: {}",
1918 .{symbol_name},
1919 ));
1920 // TODO: add a note
1921 new_export.status = .failed;
1922 return;
1923 }
1924
1925 try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export);
1926 self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
1309 error.OutOfMemory => return error.OutOfMemory,1927 error.OutOfMemory => return error.OutOfMemory,
1310 else => {1928 else => {
1311 try self.failed_exports.ensureCapacity(self.failed_exports.size + 1);1929 try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
1312 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(1930 self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
1313 self.allocator,1931 self.gpa,
1314 export_inst.base.src,1932 src,
1315 "unable to export: {}",1933 "unable to export: {}",
1316 .{@errorName(err)},1934 .{@errorName(err)},
1317 ));1935 ));
...@@ -1320,7 +1938,6 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In...@@ -1320,7 +1938,6 @@ fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) In
1320 };1938 };
1321}1939}
13221940
1323/// TODO should not need the cast on the last parameter at the callsites
1324fn addNewInstArgs(1941fn addNewInstArgs(
1325 self: *Module,1942 self: *Module,
1326 block: *Scope.Block,1943 block: *Scope.Block,
...@@ -1334,6 +1951,64 @@ fn addNewInstArgs(...@@ -1334,6 +1951,64 @@ fn addNewInstArgs(
1334 return &inst.base;1951 return &inst.base;
1335}1952}
13361953
1954fn newZIRInst(
1955 gpa: *Allocator,
1956 src: usize,
1957 comptime T: type,
1958 positionals: std.meta.fieldInfo(T, "positionals").field_type,
1959 kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
1960) !*T {
1961 const inst = try gpa.create(T);
1962 inst.* = .{
1963 .base = .{
1964 .tag = T.base_tag,
1965 .src = src,
1966 },
1967 .positionals = positionals,
1968 .kw_args = kw_args,
1969 };
1970 return inst;
1971}
1972
1973pub fn addZIRInstSpecial(
1974 self: *Module,
1975 scope: *Scope,
1976 src: usize,
1977 comptime T: type,
1978 positionals: std.meta.fieldInfo(T, "positionals").field_type,
1979 kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
1980) !*T {
1981 const gen_zir = scope.getGenZIR();
1982 try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1);
1983 const inst = try newZIRInst(gen_zir.arena, src, T, positionals, kw_args);
1984 gen_zir.instructions.appendAssumeCapacity(&inst.base);
1985 return inst;
1986}
1987
1988pub fn addZIRInst(
1989 self: *Module,
1990 scope: *Scope,
1991 src: usize,
1992 comptime T: type,
1993 positionals: std.meta.fieldInfo(T, "positionals").field_type,
1994 kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
1995) !*zir.Inst {
1996 const inst_special = try self.addZIRInstSpecial(scope, src, T, positionals, kw_args);
1997 return &inst_special.base;
1998}
1999
2000/// TODO The existence of this function is a workaround for a bug in stage1.
2001pub fn addZIRInstConst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
2002 const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
2003 return self.addZIRInst(scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
2004}
2005
2006/// TODO The existence of this function is a workaround for a bug in stage1.
2007pub fn addZIRInstBlock(self: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block {
2008 const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;
2009 return self.addZIRInstSpecial(scope, src, zir.Inst.Block, P{ .body = body }, .{});
2010}
2011
1337fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T {2012fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T {
1338 const inst = try block.arena.create(T);2013 const inst = try block.arena.create(T);
1339 inst.* = .{2014 inst.* = .{
...@@ -1344,7 +2019,7 @@ fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime...@@ -1344,7 +2019,7 @@ fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime
1344 },2019 },
1345 .args = undefined,2020 .args = undefined,
1346 };2021 };
1347 try block.instructions.append(self.allocator, &inst.base);2022 try block.instructions.append(self.gpa, &inst.base);
1348 return inst;2023 return inst;
1349}2024}
13502025
...@@ -1361,19 +2036,6 @@ fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue)...@@ -1361,19 +2036,6 @@ fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue)
1361 return &const_inst.base;2036 return &const_inst.base;
1362}2037}
13632038
1364fn constStr(self: *Module, scope: *Scope, src: usize, str: []const u8) !*Inst {
1365 const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
1366 ty_payload.* = .{ .len = str.len };
1367
1368 const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
1369 bytes_payload.* = .{ .data = str };
1370
1371 return self.constInst(scope, src, .{
1372 .ty = Type.initPayload(&ty_payload.base),
1373 .val = Value.initPayload(&bytes_payload.base),
1374 });
1375}
1376
1377fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {2039fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
1378 return self.constInst(scope, src, .{2040 return self.constInst(scope, src, .{
1379 .ty = Type.initTag(.type),2041 .ty = Type.initTag(.type),
...@@ -1388,6 +2050,13 @@ fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {...@@ -1388,6 +2050,13 @@ fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
1388 });2050 });
1389}2051}
13902052
2053fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
2054 return self.constInst(scope, src, .{
2055 .ty = Type.initTag(.noreturn),
2056 .val = Value.initTag(.the_one_possible_value),
2057 });
2058}
2059
1391fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {2060fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
1392 return self.constInst(scope, src, .{2061 return self.constInst(scope, src, .{
1393 .ty = ty,2062 .ty = ty,
...@@ -1451,7 +2120,7 @@ fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigI...@@ -1451,7 +2120,7 @@ fn constIntBig(self: *Module, scope: *Scope, src: usize, ty: Type, big_int: BigI
1451 });2120 });
1452}2121}
14532122
1454fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {2123fn analyzeConstInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue {
1455 const new_inst = try self.analyzeInst(scope, old_inst);2124 const new_inst = try self.analyzeInst(scope, old_inst);
1456 return TypedValue{2125 return TypedValue{
1457 .ty = new_inst.ty,2126 .ty = new_inst.ty,
...@@ -1459,24 +2128,33 @@ fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerErro...@@ -1459,24 +2128,33 @@ fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerErro
1459 };2128 };
1460}2129}
14612130
2131fn analyzeInstConst(self: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst {
2132 // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions
2133 // after analysis.
2134 const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena());
2135 return self.constInst(scope, const_inst.base.src, typed_value_copy);
2136}
2137
1462fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {2138fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
1463 switch (old_inst.tag) {2139 switch (old_inst.tag) {
2140 .arg => return self.analyzeInstArg(scope, old_inst.cast(zir.Inst.Arg).?),
2141 .block => return self.analyzeInstBlock(scope, old_inst.cast(zir.Inst.Block).?),
2142 .@"break" => return self.analyzeInstBreak(scope, old_inst.cast(zir.Inst.Break).?),
1464 .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?),2143 .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?),
2144 .breakvoid => return self.analyzeInstBreakVoid(scope, old_inst.cast(zir.Inst.BreakVoid).?),
1465 .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?),2145 .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?),
1466 .compileerror => return self.analyzeInstCompileError(scope, old_inst.cast(zir.Inst.CompileError).?),2146 .compileerror => return self.analyzeInstCompileError(scope, old_inst.cast(zir.Inst.CompileError).?),
2147 .@"const" => return self.analyzeInstConst(scope, old_inst.cast(zir.Inst.Const).?),
1467 .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?),2148 .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?),
2149 .declref_str => return self.analyzeInstDeclRefStr(scope, old_inst.cast(zir.Inst.DeclRefStr).?),
1468 .declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?),2150 .declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?),
1469 .str => {2151 .declval_in_module => return self.analyzeInstDeclValInModule(scope, old_inst.cast(zir.Inst.DeclValInModule).?),
1470 const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes;2152 .str => return self.analyzeInstStr(scope, old_inst.cast(zir.Inst.Str).?),
1471 // The bytes references memory inside the ZIR module, which can get deallocated
1472 // after semantic analysis is complete. We need the memory to be in the Decl's arena.
1473 const arena_bytes = try scope.arena().dupe(u8, bytes);
1474 return self.constStr(scope, old_inst.src, arena_bytes);
1475 },
1476 .int => {2153 .int => {
1477 const big_int = old_inst.cast(zir.Inst.Int).?.positionals.int;2154 const big_int = old_inst.cast(zir.Inst.Int).?.positionals.int;
1478 return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);2155 return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
1479 },2156 },
2157 .inttype => return self.analyzeInstIntType(scope, old_inst.cast(zir.Inst.IntType).?),
1480 .ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.cast(zir.Inst.PtrToInt).?),2158 .ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.cast(zir.Inst.PtrToInt).?),
1481 .fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.cast(zir.Inst.FieldPtr).?),2159 .fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.cast(zir.Inst.FieldPtr).?),
1482 .deref => return self.analyzeInstDeref(scope, old_inst.cast(zir.Inst.Deref).?),2160 .deref => return self.analyzeInstDeref(scope, old_inst.cast(zir.Inst.Deref).?),
...@@ -1484,58 +2162,220 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In...@@ -1484,58 +2162,220 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
1484 .@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(zir.Inst.Asm).?),2162 .@"asm" => return self.analyzeInstAsm(scope, old_inst.cast(zir.Inst.Asm).?),
1485 .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(zir.Inst.Unreachable).?),2163 .@"unreachable" => return self.analyzeInstUnreachable(scope, old_inst.cast(zir.Inst.Unreachable).?),
1486 .@"return" => return self.analyzeInstRet(scope, old_inst.cast(zir.Inst.Return).?),2164 .@"return" => return self.analyzeInstRet(scope, old_inst.cast(zir.Inst.Return).?),
2165 .returnvoid => return self.analyzeInstRetVoid(scope, old_inst.cast(zir.Inst.ReturnVoid).?),
1487 .@"fn" => return self.analyzeInstFn(scope, old_inst.cast(zir.Inst.Fn).?),2166 .@"fn" => return self.analyzeInstFn(scope, old_inst.cast(zir.Inst.Fn).?),
1488 .@"export" => {2167 .@"export" => return self.analyzeInstExport(scope, old_inst.cast(zir.Inst.Export).?),
1489 try self.analyzeExport(scope, old_inst.cast(zir.Inst.Export).?);
1490 return self.constVoid(scope, old_inst.src);
1491 },
1492 .primitive => return self.analyzeInstPrimitive(scope, old_inst.cast(zir.Inst.Primitive).?),2168 .primitive => return self.analyzeInstPrimitive(scope, old_inst.cast(zir.Inst.Primitive).?),
1493 .ref => return self.analyzeInstRef(scope, old_inst.cast(zir.Inst.Ref).?),
1494 .fntype => return self.analyzeInstFnType(scope, old_inst.cast(zir.Inst.FnType).?),2169 .fntype => return self.analyzeInstFnType(scope, old_inst.cast(zir.Inst.FnType).?),
1495 .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(zir.Inst.IntCast).?),2170 .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(zir.Inst.IntCast).?),
1496 .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(zir.Inst.BitCast).?),2171 .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(zir.Inst.BitCast).?),
1497 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.cast(zir.Inst.ElemPtr).?),2172 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.cast(zir.Inst.ElemPtr).?),
1498 .add => return self.analyzeInstAdd(scope, old_inst.cast(zir.Inst.Add).?),2173 .add => return self.analyzeInstAdd(scope, old_inst.cast(zir.Inst.Add).?),
2174 .sub => return self.analyzeInstSub(scope, old_inst.cast(zir.Inst.Sub).?),
1499 .cmp => return self.analyzeInstCmp(scope, old_inst.cast(zir.Inst.Cmp).?),2175 .cmp => return self.analyzeInstCmp(scope, old_inst.cast(zir.Inst.Cmp).?),
1500 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?),2176 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?),
1501 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?),2177 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?),
1502 .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(zir.Inst.IsNonNull).?),2178 .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(zir.Inst.IsNonNull).?),
2179 .boolnot => return self.analyzeInstBoolNot(scope, old_inst.cast(zir.Inst.BoolNot).?),
2180 }
2181}
2182
2183fn analyzeInstStr(self: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst {
2184 // The bytes references memory inside the ZIR module, which can get deallocated
2185 // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena.
2186 var new_decl_arena = std.heap.ArenaAllocator.init(self.gpa);
2187 const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes);
2188
2189 const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
2190 ty_payload.* = .{ .len = arena_bytes.len };
2191
2192 const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
2193 bytes_payload.* = .{ .data = arena_bytes };
2194
2195 const new_decl = try self.createAnonymousDecl(scope, &new_decl_arena, .{
2196 .ty = Type.initPayload(&ty_payload.base),
2197 .val = Value.initPayload(&bytes_payload.base),
2198 });
2199 return self.analyzeDeclRef(scope, str_inst.base.src, new_decl);
2200}
2201
2202fn createAnonymousDecl(
2203 self: *Module,
2204 scope: *Scope,
2205 decl_arena: *std.heap.ArenaAllocator,
2206 typed_value: TypedValue,
2207) !*Decl {
2208 const name_index = self.getNextAnonNameIndex();
2209 const scope_decl = scope.decl().?;
2210 const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index });
2211 defer self.gpa.free(name);
2212 const name_hash = scope.namespace().fullyQualifiedNameHash(name);
2213 const src_hash: std.zig.SrcHash = undefined;
2214 const new_decl = try self.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash);
2215 const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
2216
2217 decl_arena_state.* = decl_arena.state;
2218 new_decl.typed_value = .{
2219 .most_recent = .{
2220 .typed_value = typed_value,
2221 .arena = decl_arena_state,
2222 },
2223 };
2224 new_decl.analysis = .complete;
2225 new_decl.generation = self.generation;
2226
2227 // TODO: This generates the Decl into the machine code file if it is of a type that is non-zero size.
2228 // We should be able to further improve the compiler to not omit Decls which are only referenced at
2229 // compile-time and not runtime.
2230 if (typed_value.ty.hasCodeGenBits()) {
2231 try self.bin_file.allocateDeclIndexes(new_decl);
2232 try self.work_queue.writeItem(.{ .codegen_decl = new_decl });
1503 }2233 }
2234
2235 return new_decl;
2236}
2237
2238fn getNextAnonNameIndex(self: *Module) usize {
2239 return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
2240}
2241
2242pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
2243 const namespace = scope.namespace();
2244 const name_hash = namespace.fullyQualifiedNameHash(ident_name);
2245 return self.decl_table.get(name_hash);
2246}
2247
2248fn analyzeInstExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst {
2249 const symbol_name = try self.resolveConstString(scope, export_inst.positionals.symbol_name);
2250 const exported_decl = self.lookupDeclName(scope, export_inst.positionals.decl_name) orelse
2251 return self.fail(scope, export_inst.base.src, "decl '{}' not found", .{export_inst.positionals.decl_name});
2252 try self.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl);
2253 return self.constVoid(scope, export_inst.base.src);
1504}2254}
15052255
1506fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {2256fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {
1507 return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});2257 return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
1508}2258}
15092259
2260fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
2261 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2262 const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
2263 const param_index = b.instructions.items.len;
2264 const param_count = fn_ty.fnParamLen();
2265 if (param_index >= param_count) {
2266 return self.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{
2267 param_index,
2268 param_count,
2269 });
2270 }
2271 const param_type = fn_ty.fnParamType(param_index);
2272 return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, {});
2273}
2274
2275fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {
2276 const parent_block = scope.cast(Scope.Block).?;
2277
2278 // Reserve space for a Block instruction so that generated Break instructions can
2279 // point to it, even if it doesn't end up getting used because the code ends up being
2280 // comptime evaluated.
2281 const block_inst = try parent_block.arena.create(Inst.Block);
2282 block_inst.* = .{
2283 .base = .{
2284 .tag = Inst.Block.base_tag,
2285 .ty = undefined, // Set after analysis.
2286 .src = inst.base.src,
2287 },
2288 .args = undefined,
2289 };
2290
2291 var child_block: Scope.Block = .{
2292 .parent = parent_block,
2293 .func = parent_block.func,
2294 .decl = parent_block.decl,
2295 .instructions = .{},
2296 .arena = parent_block.arena,
2297 // TODO @as here is working around a miscompilation compiler bug :(
2298 .label = @as(?Scope.Block.Label, Scope.Block.Label{
2299 .zir_block = inst,
2300 .results = .{},
2301 .block_inst = block_inst,
2302 }),
2303 };
2304 const label = &child_block.label.?;
2305
2306 defer child_block.instructions.deinit(self.gpa);
2307 defer label.results.deinit(self.gpa);
2308
2309 try self.analyzeBody(&child_block.base, inst.positionals.body);
2310
2311 // Blocks must terminate with noreturn instruction.
2312 assert(child_block.instructions.items.len != 0);
2313 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
2314
2315 // Need to set the type and emit the Block instruction. This allows machine code generation
2316 // to emit a jump instruction to after the block when it encounters the break.
2317 try parent_block.instructions.append(self.gpa, &block_inst.base);
2318 block_inst.base.ty = try self.resolvePeerTypes(scope, label.results.items);
2319 block_inst.args.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) };
2320 return &block_inst.base;
2321}
2322
1510fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {2323fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {
1511 const b = try self.requireRuntimeBlock(scope, inst.base.src);2324 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1512 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});2325 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, {});
1513}2326}
15142327
1515fn analyzeInstRef(self: *Module, scope: *Scope, inst: *zir.Inst.Ref) InnerError!*Inst {2328fn analyzeInstBreak(self: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst {
1516 const decl = try self.resolveCompleteDecl(scope, inst.positionals.operand);2329 const operand = try self.resolveInst(scope, inst.positionals.operand);
1517 return self.analyzeDeclRef(scope, inst.base.src, decl);2330 const block = inst.positionals.block;
2331 return self.analyzeBreak(scope, inst.base.src, block, operand);
1518}2332}
15192333
1520fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {2334fn analyzeInstBreakVoid(self: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst {
2335 const block = inst.positionals.block;
2336 const void_inst = try self.constVoid(scope, inst.base.src);
2337 return self.analyzeBreak(scope, inst.base.src, block, void_inst);
2338}
2339
2340fn analyzeBreak(
2341 self: *Module,
2342 scope: *Scope,
2343 src: usize,
2344 zir_block: *zir.Inst.Block,
2345 operand: *Inst,
2346) InnerError!*Inst {
2347 var opt_block = scope.cast(Scope.Block);
2348 while (opt_block) |block| {
2349 if (block.label) |*label| {
2350 if (label.zir_block == zir_block) {
2351 try label.results.append(self.gpa, operand);
2352 const b = try self.requireRuntimeBlock(scope, src);
2353 return self.addNewInstArgs(b, src, Type.initTag(.noreturn), Inst.Br, .{
2354 .block = label.block_inst,
2355 .operand = operand,
2356 });
2357 }
2358 }
2359 opt_block = block.parent;
2360 } else unreachable;
2361}
2362
2363fn analyzeInstDeclRefStr(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst {
1521 const decl_name = try self.resolveConstString(scope, inst.positionals.name);2364 const decl_name = try self.resolveConstString(scope, inst.positionals.name);
1522 // This will need to get more fleshed out when there are proper structs & namespaces.2365 return self.analyzeDeclRefByName(scope, inst.base.src, decl_name);
1523 const zir_module = scope.namespace();2366}
1524 const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
1525 return self.fail(scope, inst.positionals.name.src, "use of undeclared identifier '{}'", .{decl_name});
15262367
1527 const decl = try self.resolveCompleteDecl(scope, src_decl);2368fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
1528 return self.analyzeDeclRef(scope, inst.base.src, decl);2369 return self.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name);
1529}2370}
15302371
1531fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl {2372fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl {
1532 const decl_name = inst.positionals.name;2373 const decl_name = inst.positionals.name;
1533 // This will need to get more fleshed out when there are proper structs & namespaces.2374 const zir_module = scope.namespace().cast(Scope.ZIRModule).?;
1534 const zir_module = scope.namespace();
1535 const src_decl = zir_module.contents.module.findDecl(decl_name) orelse2375 const src_decl = zir_module.contents.module.findDecl(decl_name) orelse
1536 return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name});2376 return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name});
15372377
1538 const decl = try self.resolveCompleteDecl(scope, src_decl);2378 const decl = try self.resolveCompleteZirDecl(scope, src_decl.decl);
15392379
1540 return decl;2380 return decl;
1541}2381}
...@@ -1546,18 +2386,46 @@ fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) Inn...@@ -1546,18 +2386,46 @@ fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) Inn
1546 return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);2386 return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
1547}2387}
15482388
2389fn analyzeInstDeclValInModule(self: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst {
2390 const decl = inst.positionals.decl;
2391 const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl);
2392 return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src);
2393}
2394
1549fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {2395fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
2396 const scope_decl = scope.decl().?;
2397 try self.declareDeclDependency(scope_decl, decl);
2398 self.ensureDeclAnalyzed(decl) catch |err| {
2399 if (scope.cast(Scope.Block)) |block| {
2400 if (block.func) |func| {
2401 func.analysis = .dependency_failure;
2402 } else {
2403 block.decl.analysis = .dependency_failure;
2404 }
2405 } else {
2406 scope_decl.analysis = .dependency_failure;
2407 }
2408 return err;
2409 };
2410
1550 const decl_tv = try decl.typedValue();2411 const decl_tv = try decl.typedValue();
1551 const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);2412 const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
1552 ty_payload.* = .{ .pointee_type = decl_tv.ty };2413 ty_payload.* = .{ .pointee_type = decl_tv.ty };
1553 const val_payload = try scope.arena().create(Value.Payload.DeclRef);2414 const val_payload = try scope.arena().create(Value.Payload.DeclRef);
1554 val_payload.* = .{ .decl = decl };2415 val_payload.* = .{ .decl = decl };
2416
1555 return self.constInst(scope, src, .{2417 return self.constInst(scope, src, .{
1556 .ty = Type.initPayload(&ty_payload.base),2418 .ty = Type.initPayload(&ty_payload.base),
1557 .val = Value.initPayload(&val_payload.base),2419 .val = Value.initPayload(&val_payload.base),
1558 });2420 });
1559}2421}
15602422
2423fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
2424 const decl = self.lookupDeclName(scope, decl_name) orelse
2425 return self.fail(scope, src, "decl '{}' not found", .{decl_name});
2426 return self.analyzeDeclRef(scope, src, decl);
2427}
2428
1561fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {2429fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
1562 const func = try self.resolveInst(scope, inst.positionals.func);2430 const func = try self.resolveInst(scope, inst.positionals.func);
1563 if (func.ty.zigTypeTag() != .Fn)2431 if (func.ty.zigTypeTag() != .Fn)
...@@ -1605,8 +2473,8 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro...@@ -1605,8 +2473,8 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro
16052473
1606 // TODO handle function calls of generic functions2474 // TODO handle function calls of generic functions
16072475
1608 const fn_param_types = try self.allocator.alloc(Type, fn_params_len);2476 const fn_param_types = try self.gpa.alloc(Type, fn_params_len);
1609 defer self.allocator.free(fn_param_types);2477 defer self.gpa.free(fn_param_types);
1610 func.ty.fnParamTypes(fn_param_types);2478 func.ty.fnParamTypes(fn_param_types);
16112479
1612 const casted_args = try scope.arena().alloc(*Inst, fn_params_len);2480 const casted_args = try scope.arena().alloc(*Inst, fn_params_len);
...@@ -1616,7 +2484,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro...@@ -1616,7 +2484,7 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro
1616 }2484 }
16172485
1618 const b = try self.requireRuntimeBlock(scope, inst.base.src);2486 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1619 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, Inst.Args(Inst.Call){2487 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Call, .{
1620 .func = func,2488 .func = func,
1621 .args = casted_args,2489 .args = casted_args,
1622 });2490 });
...@@ -1624,10 +2492,22 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro...@@ -1624,10 +2492,22 @@ fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerErro
16242492
1625fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {2493fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst {
1626 const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type);2494 const fn_type = try self.resolveType(scope, fn_inst.positionals.fn_type);
2495 const fn_zir = blk: {
2496 var fn_arena = std.heap.ArenaAllocator.init(self.gpa);
2497 errdefer fn_arena.deinit();
2498
2499 const fn_zir = try scope.arena().create(Fn.ZIR);
2500 fn_zir.* = .{
2501 .body = .{
2502 .instructions = fn_inst.positionals.body.instructions,
2503 },
2504 .arena = fn_arena.state,
2505 };
2506 break :blk fn_zir;
2507 };
1627 const new_func = try scope.arena().create(Fn);2508 const new_func = try scope.arena().create(Fn);
1628 new_func.* = .{2509 new_func.* = .{
1629 .fn_type = fn_type,2510 .analysis = .{ .queued = fn_zir },
1630 .analysis = .{ .queued = fn_inst },
1631 .owner_decl = scope.decl().?,2511 .owner_decl = scope.decl().?,
1632 };2512 };
1633 const fn_payload = try scope.arena().create(Value.Payload.Function);2513 const fn_payload = try scope.arena().create(Value.Payload.Function);
...@@ -1638,31 +2518,45 @@ fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError...@@ -1638,31 +2518,45 @@ fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError
1638 });2518 });
1639}2519}
16402520
2521fn analyzeInstIntType(self: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst {
2522 return self.fail(scope, inttype.base.src, "TODO implement inttype", .{});
2523}
2524
1641fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {2525fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
1642 const return_type = try self.resolveType(scope, fntype.positionals.return_type);2526 const return_type = try self.resolveType(scope, fntype.positionals.return_type);
16432527
1644 if (return_type.zigTypeTag() == .NoReturn and2528 // Hot path for some common function types.
1645 fntype.positionals.param_types.len == 0 and2529 if (fntype.positionals.param_types.len == 0) {
1646 fntype.kw_args.cc == .Unspecified)2530 if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Unspecified) {
1647 {2531 return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));
1648 return self.constType(scope, fntype.base.src, Type.initTag(.fn_noreturn_no_args));2532 }
1649 }
16502533
1651 if (return_type.zigTypeTag() == .NoReturn and2534 if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .Unspecified) {
1652 fntype.positionals.param_types.len == 0 and2535 return self.constType(scope, fntype.base.src, Type.initTag(.fn_void_no_args));
1653 fntype.kw_args.cc == .Naked)2536 }
1654 {2537
1655 return self.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));2538 if (return_type.zigTypeTag() == .NoReturn and fntype.kw_args.cc == .Naked) {
2539 return self.constType(scope, fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args));
2540 }
2541
2542 if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .C) {
2543 return self.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
2544 }
1656 }2545 }
16572546
1658 if (return_type.zigTypeTag() == .Void and2547 const arena = scope.arena();
1659 fntype.positionals.param_types.len == 0 and2548 const param_types = try arena.alloc(Type, fntype.positionals.param_types.len);
1660 fntype.kw_args.cc == .C)2549 for (fntype.positionals.param_types) |param_type, i| {
1661 {2550 param_types[i] = try self.resolveType(scope, param_type);
1662 return self.constType(scope, fntype.base.src, Type.initTag(.fn_ccc_void_no_args));
1663 }2551 }
16642552
1665 return self.fail(scope, fntype.base.src, "TODO implement fntype instruction more", .{});2553 const payload = try arena.create(Type.Payload.Function);
2554 payload.* = .{
2555 .cc = fntype.kw_args.cc,
2556 .return_type = return_type,
2557 .param_types = param_types,
2558 };
2559 return self.constType(scope, fntype.base.src, Type.initPayload(&payload.base));
1666}2560}
16672561
1668fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {2562fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
...@@ -1683,7 +2577,7 @@ fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToIn...@@ -1683,7 +2577,7 @@ fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToIn
1683 // TODO handle known-pointer-address2577 // TODO handle known-pointer-address
1684 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);2578 const b = try self.requireRuntimeBlock(scope, ptrtoint.base.src);
1685 const ty = Type.initTag(.usize);2579 const ty = Type.initTag(.usize);
1686 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr });2580 return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, .{ .ptr = ptr });
1687}2581}
16882582
1689fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {2583fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
...@@ -1788,11 +2682,24 @@ fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inn...@@ -1788,11 +2682,24 @@ fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inn
1788 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});2682 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
1789}2683}
17902684
2685fn analyzeInstSub(self: *Module, scope: *Scope, inst: *zir.Inst.Sub) InnerError!*Inst {
2686 return self.fail(scope, inst.base.src, "TODO implement analysis of sub", .{});
2687}
2688
1791fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!*Inst {2689fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!*Inst {
2690 const tracy = trace(@src());
2691 defer tracy.end();
2692
1792 const lhs = try self.resolveInst(scope, inst.positionals.lhs);2693 const lhs = try self.resolveInst(scope, inst.positionals.lhs);
1793 const rhs = try self.resolveInst(scope, inst.positionals.rhs);2694 const rhs = try self.resolveInst(scope, inst.positionals.rhs);
17942695
1795 if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) {2696 if ((lhs.ty.zigTypeTag() == .Int or lhs.ty.zigTypeTag() == .ComptimeInt) and
2697 (rhs.ty.zigTypeTag() == .Int or rhs.ty.zigTypeTag() == .ComptimeInt))
2698 {
2699 if (!lhs.ty.eql(rhs.ty)) {
2700 return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{});
2701 }
2702
1796 if (lhs.value()) |lhs_val| {2703 if (lhs.value()) |lhs_val| {
1797 if (rhs.value()) |rhs_val| {2704 if (rhs.value()) |rhs_val| {
1798 // TODO is this a performance issue? maybe we should try the operation without2705 // TODO is this a performance issue? maybe we should try the operation without
...@@ -1809,10 +2716,6 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!...@@ -1809,10 +2716,6 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!
1809 result_bigint.add(lhs_bigint, rhs_bigint);2716 result_bigint.add(lhs_bigint, rhs_bigint);
1810 const result_limbs = result_bigint.limbs[0..result_bigint.len];2717 const result_limbs = result_bigint.limbs[0..result_bigint.len];
18112718
1812 if (!lhs.ty.eql(rhs.ty)) {
1813 return self.fail(scope, inst.base.src, "TODO implement peer type resolution", .{});
1814 }
1815
1816 const val_payload = if (result_bigint.positive) blk: {2719 const val_payload = if (result_bigint.positive) blk: {
1817 const val_payload = try scope.arena().create(Value.Payload.IntBigPositive);2720 const val_payload = try scope.arena().create(Value.Payload.IntBigPositive);
1818 val_payload.* = .{ .limbs = result_limbs };2721 val_payload.* = .{ .limbs = result_limbs };
...@@ -1829,9 +2732,14 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!...@@ -1829,9 +2732,14 @@ fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!
1829 });2732 });
1830 }2733 }
1831 }2734 }
1832 }
18332735
1834 return self.fail(scope, inst.base.src, "TODO implement more analyze add", .{});2736 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2737 return self.addNewInstArgs(b, inst.base.src, lhs.ty, Inst.Add, .{
2738 .lhs = lhs,
2739 .rhs = rhs,
2740 });
2741 }
2742 return self.fail(scope, inst.base.src, "TODO analyze add for {} + {}", .{ lhs.ty.zigTypeTag(), rhs.ty.zigTypeTag() });
1835}2743}
18362744
1837fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.Deref) InnerError!*Inst {2745fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.Deref) InnerError!*Inst {
...@@ -1875,7 +2783,7 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr...@@ -1875,7 +2783,7 @@ fn analyzeInstAsm(self: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerEr
1875 }2783 }
18762784
1877 const b = try self.requireRuntimeBlock(scope, assembly.base.src);2785 const b = try self.requireRuntimeBlock(scope, assembly.base.src);
1878 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){2786 return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, .{
1879 .asm_source = asm_source,2787 .asm_source = asm_source,
1880 .is_volatile = assembly.kw_args.@"volatile",2788 .is_volatile = assembly.kw_args.@"volatile",
1881 .output = output,2789 .output = output,
...@@ -1911,20 +2819,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!...@@ -1911,20 +2819,12 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!
1911 }2819 }
1912 const b = try self.requireRuntimeBlock(scope, inst.base.src);2820 const b = try self.requireRuntimeBlock(scope, inst.base.src);
1913 switch (op) {2821 switch (op) {
1914 .eq => return self.addNewInstArgs(2822 .eq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNull, .{
1915 b,2823 .operand = opt_operand,
1916 inst.base.src,2824 }),
1917 Type.initTag(.bool),2825 .neq => return self.addNewInstArgs(b, inst.base.src, Type.initTag(.bool), Inst.IsNonNull, .{
1918 Inst.IsNull,2826 .operand = opt_operand,
1919 Inst.Args(Inst.IsNull){ .operand = opt_operand },2827 }),
1920 ),
1921 .neq => return self.addNewInstArgs(
1922 b,
1923 inst.base.src,
1924 Type.initTag(.bool),
1925 Inst.IsNonNull,
1926 Inst.Args(Inst.IsNonNull){ .operand = opt_operand },
1927 ),
1928 else => unreachable,2828 else => unreachable,
1929 }2829 }
1930 } else if (is_equality_cmp and2830 } else if (is_equality_cmp and
...@@ -1953,6 +2853,17 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!...@@ -1953,6 +2853,17 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError!
1953 return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});2853 return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{});
1954}2854}
19552855
2856fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.BoolNot) InnerError!*Inst {
2857 const uncasted_operand = try self.resolveInst(scope, inst.positionals.operand);
2858 const bool_type = Type.initTag(.bool);
2859 const operand = try self.coerce(scope, bool_type, uncasted_operand);
2860 if (try self.resolveDefinedValue(scope, operand)) |val| {
2861 return self.constBool(scope, inst.base.src, !val.toBool());
2862 }
2863 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2864 return self.addNewInstArgs(b, inst.base.src, bool_type, Inst.Not, .{ .operand = operand });
2865}
2866
1956fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {2867fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst {
1957 const operand = try self.resolveInst(scope, inst.positionals.operand);2868 const operand = try self.resolveInst(scope, inst.positionals.operand);
1958 return self.analyzeIsNull(scope, inst.base.src, operand, true);2869 return self.analyzeIsNull(scope, inst.base.src, operand, true);
...@@ -1976,24 +2887,26 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner...@@ -1976,24 +2887,26 @@ fn analyzeInstCondBr(self: *Module, scope: *Scope, inst: *zir.Inst.CondBr) Inner
1976 const parent_block = try self.requireRuntimeBlock(scope, inst.base.src);2887 const parent_block = try self.requireRuntimeBlock(scope, inst.base.src);
19772888
1978 var true_block: Scope.Block = .{2889 var true_block: Scope.Block = .{
2890 .parent = parent_block,
1979 .func = parent_block.func,2891 .func = parent_block.func,
1980 .decl = parent_block.decl,2892 .decl = parent_block.decl,
1981 .instructions = .{},2893 .instructions = .{},
1982 .arena = parent_block.arena,2894 .arena = parent_block.arena,
1983 };2895 };
1984 defer true_block.instructions.deinit(self.allocator);2896 defer true_block.instructions.deinit(self.gpa);
1985 try self.analyzeBody(&true_block.base, inst.positionals.true_body);2897 try self.analyzeBody(&true_block.base, inst.positionals.true_body);
19862898
1987 var false_block: Scope.Block = .{2899 var false_block: Scope.Block = .{
2900 .parent = parent_block,
1988 .func = parent_block.func,2901 .func = parent_block.func,
1989 .decl = parent_block.decl,2902 .decl = parent_block.decl,
1990 .instructions = .{},2903 .instructions = .{},
1991 .arena = parent_block.arena,2904 .arena = parent_block.arena,
1992 };2905 };
1993 defer false_block.instructions.deinit(self.allocator);2906 defer false_block.instructions.deinit(self.gpa);
1994 try self.analyzeBody(&false_block.base, inst.positionals.false_body);2907 try self.analyzeBody(&false_block.base, inst.positionals.false_body);
19952908
1996 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.void), Inst.CondBr, Inst.Args(Inst.CondBr){2909 return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.noreturn), Inst.CondBr, Inst.Args(Inst.CondBr){
1997 .condition = cond,2910 .condition = cond,
1998 .true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) },2911 .true_body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) },
1999 .false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) },2912 .false_body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) },
...@@ -2019,23 +2932,19 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea...@@ -2019,23 +2932,19 @@ fn analyzeInstUnreachable(self: *Module, scope: *Scope, unreach: *zir.Inst.Unrea
2019}2932}
20202933
2021fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {2934fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst {
2935 const operand = try self.resolveInst(scope, inst.positionals.operand);
2936 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2937 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, .{ .operand = operand });
2938}
2939
2940fn analyzeInstRetVoid(self: *Module, scope: *Scope, inst: *zir.Inst.ReturnVoid) InnerError!*Inst {
2022 const b = try self.requireRuntimeBlock(scope, inst.base.src);2941 const b = try self.requireRuntimeBlock(scope, inst.base.src);
2023 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {});2942 return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.RetVoid, {});
2024}2943}
20252944
2026fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {2945fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void {
2027 if (scope.cast(Scope.Block)) |b| {2946 for (body.instructions) |src_inst| {
2028 const analysis = b.func.analysis.in_progress;2947 src_inst.analyzed_inst = try self.analyzeInst(scope, src_inst);
2029 analysis.needed_inst_capacity += body.instructions.len;
2030 try analysis.inst_table.ensureCapacity(analysis.needed_inst_capacity);
2031 for (body.instructions) |src_inst| {
2032 const new_inst = try self.analyzeInst(scope, src_inst);
2033 analysis.inst_table.putAssumeCapacityNoClobber(src_inst, new_inst);
2034 }
2035 } else {
2036 for (body.instructions) |src_inst| {
2037 _ = try self.analyzeInst(scope, src_inst);
2038 }
2039 }2948 }
2040}2949}
20412950
...@@ -2118,7 +3027,7 @@ fn cmpNumeric(...@@ -2118,7 +3027,7 @@ fn cmpNumeric(
2118 };3027 };
2119 const casted_lhs = try self.coerce(scope, dest_type, lhs);3028 const casted_lhs = try self.coerce(scope, dest_type, lhs);
2120 const casted_rhs = try self.coerce(scope, dest_type, rhs);3029 const casted_rhs = try self.coerce(scope, dest_type, rhs);
2121 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){3030 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, .{
2122 .lhs = casted_lhs,3031 .lhs = casted_lhs,
2123 .rhs = casted_rhs,3032 .rhs = casted_rhs,
2124 .op = op,3033 .op = op,
...@@ -2148,7 +3057,7 @@ fn cmpNumeric(...@@ -2148,7 +3057,7 @@ fn cmpNumeric(
2148 return self.constUndef(scope, src, Type.initTag(.bool));3057 return self.constUndef(scope, src, Type.initTag(.bool));
2149 const is_unsigned = if (lhs_is_float) x: {3058 const is_unsigned = if (lhs_is_float) x: {
2150 var bigint_space: Value.BigIntSpace = undefined;3059 var bigint_space: Value.BigIntSpace = undefined;
2151 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.allocator);3060 var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
2152 defer bigint.deinit();3061 defer bigint.deinit();
2153 const zcmp = lhs_val.orderAgainstZero();3062 const zcmp = lhs_val.orderAgainstZero();
2154 if (lhs_val.floatHasFraction()) {3063 if (lhs_val.floatHasFraction()) {
...@@ -2183,7 +3092,7 @@ fn cmpNumeric(...@@ -2183,7 +3092,7 @@ fn cmpNumeric(
2183 return self.constUndef(scope, src, Type.initTag(.bool));3092 return self.constUndef(scope, src, Type.initTag(.bool));
2184 const is_unsigned = if (rhs_is_float) x: {3093 const is_unsigned = if (rhs_is_float) x: {
2185 var bigint_space: Value.BigIntSpace = undefined;3094 var bigint_space: Value.BigIntSpace = undefined;
2186 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.allocator);3095 var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.gpa);
2187 defer bigint.deinit();3096 defer bigint.deinit();
2188 const zcmp = rhs_val.orderAgainstZero();3097 const zcmp = rhs_val.orderAgainstZero();
2189 if (rhs_val.floatHasFraction()) {3098 if (rhs_val.floatHasFraction()) {
...@@ -2220,9 +3129,9 @@ fn cmpNumeric(...@@ -2220,9 +3129,9 @@ fn cmpNumeric(
2220 break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);3129 break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits);
2221 };3130 };
2222 const casted_lhs = try self.coerce(scope, dest_type, lhs);3131 const casted_lhs = try self.coerce(scope, dest_type, lhs);
2223 const casted_rhs = try self.coerce(scope, dest_type, lhs);3132 const casted_rhs = try self.coerce(scope, dest_type, rhs);
22243133
2225 return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){3134 return self.addNewInstArgs(b, src, Type.initTag(.bool), Inst.Cmp, .{
2226 .lhs = casted_lhs,3135 .lhs = casted_lhs,
2227 .rhs = casted_rhs,3136 .rhs = casted_rhs,
2228 .op = op,3137 .op = op,
...@@ -2241,6 +3150,31 @@ fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {...@@ -2241,6 +3150,31 @@ fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
2241 }3150 }
2242}3151}
22433152
3153fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
3154 if (instructions.len == 0)
3155 return Type.initTag(.noreturn);
3156
3157 if (instructions.len == 1)
3158 return instructions[0].ty;
3159
3160 var prev_inst = instructions[0];
3161 for (instructions[1..]) |next_inst| {
3162 if (next_inst.ty.eql(prev_inst.ty))
3163 continue;
3164 if (next_inst.ty.zigTypeTag() == .NoReturn)
3165 continue;
3166 if (prev_inst.ty.zigTypeTag() == .NoReturn) {
3167 prev_inst = next_inst;
3168 continue;
3169 }
3170
3171 // TODO error notes pointing out each type
3172 return self.fail(scope, next_inst.src, "incompatible types: '{}' and '{}'", .{ prev_inst.ty, next_inst.ty });
3173 }
3174
3175 return prev_inst.ty;
3176}
3177
2244fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {3178fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2245 // If the types are the same, we can return the operand.3179 // If the types are the same, we can return the operand.
2246 if (dest_type.eql(inst.ty))3180 if (dest_type.eql(inst.ty))
...@@ -2282,7 +3216,10 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {...@@ -2282,7 +3216,10 @@ fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2282 if (inst.value()) |val| {3216 if (inst.value()) |val| {
2283 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });3217 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2284 } else {3218 } else {
2285 return self.fail(scope, inst.src, "TODO implement runtime integer widening", .{});3219 return self.fail(scope, inst.src, "TODO implement runtime integer widening ({} to {})", .{
3220 inst.ty,
3221 dest_type,
3222 });
2286 }3223 }
2287 } else {3224 } else {
2288 return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });3225 return self.fail(scope, inst.src, "TODO implement more int widening {} to {}", .{ inst.ty, dest_type });
...@@ -2299,7 +3236,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {...@@ -2299,7 +3236,7 @@ fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2299 }3236 }
2300 // TODO validate the type size and other compile errors3237 // TODO validate the type size and other compile errors
2301 const b = try self.requireRuntimeBlock(scope, inst.src);3238 const b = try self.requireRuntimeBlock(scope, inst.src);
2302 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });3239 return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, .{ .operand = inst });
2303}3240}
23043241
2305fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {3242fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
...@@ -2310,34 +3247,77 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I...@@ -2310,34 +3247,77 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I
2310 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});3247 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
2311}3248}
23123249
2313fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: var) InnerError {3250pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
2314 @setCold(true);3251 @setCold(true);
2315 const err_msg = try ErrorMsg.create(self.allocator, src, format, args);3252 const err_msg = try ErrorMsg.create(self.gpa, src, format, args);
2316 return self.failWithOwnedErrorMsg(scope, src, err_msg);3253 return self.failWithOwnedErrorMsg(scope, src, err_msg);
2317}3254}
23183255
3256pub fn failTok(
3257 self: *Module,
3258 scope: *Scope,
3259 token_index: ast.TokenIndex,
3260 comptime format: []const u8,
3261 args: anytype,
3262) InnerError {
3263 @setCold(true);
3264 const src = scope.tree().token_locs[token_index].start;
3265 return self.fail(scope, src, format, args);
3266}
3267
3268pub fn failNode(
3269 self: *Module,
3270 scope: *Scope,
3271 ast_node: *ast.Node,
3272 comptime format: []const u8,
3273 args: anytype,
3274) InnerError {
3275 @setCold(true);
3276 const src = scope.tree().token_locs[ast_node.firstToken()].start;
3277 return self.fail(scope, src, format, args);
3278}
3279
2319fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError {3280fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError {
2320 {3281 {
2321 errdefer err_msg.destroy(self.allocator);3282 errdefer err_msg.destroy(self.gpa);
2322 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);3283 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
2323 try self.failed_files.ensureCapacity(self.failed_files.size + 1);3284 try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
2324 }3285 }
2325 switch (scope.tag) {3286 switch (scope.tag) {
2326 .decl => {3287 .decl => {
2327 const decl = scope.cast(Scope.DeclAnalysis).?.decl;3288 const decl = scope.cast(Scope.DeclAnalysis).?.decl;
2328 decl.analysis = .sema_failure;3289 decl.analysis = .sema_failure;
3290 decl.generation = self.generation;
2329 self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);3291 self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
2330 },3292 },
2331 .block => {3293 .block => {
2332 const block = scope.cast(Scope.Block).?;3294 const block = scope.cast(Scope.Block).?;
2333 block.func.analysis = .sema_failure;3295 if (block.func) |func| {
3296 func.analysis = .sema_failure;
3297 } else {
3298 block.decl.analysis = .sema_failure;
3299 block.decl.generation = self.generation;
3300 }
2334 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);3301 self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
2335 },3302 },
3303 .gen_zir => {
3304 const gen_zir = scope.cast(Scope.GenZIR).?;
3305 gen_zir.decl.analysis = .sema_failure;
3306 gen_zir.decl.generation = self.generation;
3307 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
3308 },
3309 .local_var => {
3310 const gen_zir = scope.cast(Scope.LocalVar).?.gen_zir;
3311 gen_zir.decl.analysis = .sema_failure;
3312 gen_zir.decl.generation = self.generation;
3313 self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
3314 },
2336 .zir_module => {3315 .zir_module => {
2337 const zir_module = scope.cast(Scope.ZIRModule).?;3316 const zir_module = scope.cast(Scope.ZIRModule).?;
2338 zir_module.status = .loaded_sema_failure;3317 zir_module.status = .loaded_sema_failure;
2339 self.failed_files.putAssumeCapacityNoClobber(zir_module, err_msg);3318 self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
2340 },3319 },
3320 .file => unreachable,
2341 }3321 }
2342 return error.AnalysisFail;3322 return error.AnalysisFail;
2343}3323}
...@@ -2360,28 +3340,32 @@ pub const ErrorMsg = struct {...@@ -2360,28 +3340,32 @@ pub const ErrorMsg = struct {
2360 byte_offset: usize,3340 byte_offset: usize,
2361 msg: []const u8,3341 msg: []const u8,
23623342
2363 pub fn create(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !*ErrorMsg {3343 pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg {
2364 const self = try allocator.create(ErrorMsg);3344 const self = try gpa.create(ErrorMsg);
2365 errdefer allocator.destroy(self);3345 errdefer gpa.destroy(self);
2366 self.* = try init(allocator, byte_offset, format, args);3346 self.* = try init(gpa, byte_offset, format, args);
2367 return self;3347 return self;
2368 }3348 }
23693349
2370 /// Assumes the ErrorMsg struct and msg were both allocated with allocator.3350 /// Assumes the ErrorMsg struct and msg were both allocated with allocator.
2371 pub fn destroy(self: *ErrorMsg, allocator: *Allocator) void {3351 pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
2372 self.deinit(allocator);3352 self.deinit(gpa);
2373 allocator.destroy(self);3353 gpa.destroy(self);
2374 }3354 }
23753355
2376 pub fn init(allocator: *Allocator, byte_offset: usize, comptime format: []const u8, args: var) !ErrorMsg {3356 pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg {
2377 return ErrorMsg{3357 return ErrorMsg{
2378 .byte_offset = byte_offset,3358 .byte_offset = byte_offset,
2379 .msg = try std.fmt.allocPrint(allocator, format, args),3359 .msg = try std.fmt.allocPrint(gpa, format, args),
2380 };3360 };
2381 }3361 }
23823362
2383 pub fn deinit(self: *ErrorMsg, allocator: *Allocator) void {3363 pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void {
2384 allocator.free(self.msg);3364 gpa.free(self.msg);
2385 self.* = undefined;3365 self.* = undefined;
2386 }3366 }
2387};3367};
3368
3369fn srcHashEql(a: std.zig.SrcHash, b: std.zig.SrcHash) bool {
3370 return @bitCast(u128, a) == @bitCast(u128, b);
3371}
src-self-hosted/TypedValue.zig+8
...@@ -21,3 +21,11 @@ pub const Managed = struct {...@@ -21,3 +21,11 @@ pub const Managed = struct {
21 self.* = undefined;21 self.* = undefined;
22 }22 }
23};23};
24
25/// Assumes arena allocation. Does a recursive copy.
26pub fn copy(self: TypedValue, allocator: *Allocator) error{OutOfMemory}!TypedValue {
27 return TypedValue{
28 .ty = try self.ty.copy(allocator),
29 .val = try self.val.copy(allocator),
30 };
31}
src-self-hosted/astgen.zig created+643
...@@ -0,0 +1,643 @@
1const std = @import("std");
2const mem = std.mem;
3const Value = @import("value.zig").Value;
4const Type = @import("type.zig").Type;
5const TypedValue = @import("TypedValue.zig");
6const assert = std.debug.assert;
7const zir = @import("zir.zig");
8const Module = @import("Module.zig");
9const ast = std.zig.ast;
10const trace = @import("tracy.zig").trace;
11const Scope = Module.Scope;
12const InnerError = Module.InnerError;
13
14/// Turn Zig AST into untyped ZIR istructions.
15pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
16 switch (node.tag) {
17 .VarDecl => unreachable, // Handled in `blockExpr`.
18
19 .Identifier => return identifier(mod, scope, node.castTag(.Identifier).?),
20 .Asm => return assembly(mod, scope, node.castTag(.Asm).?),
21 .StringLiteral => return stringLiteral(mod, scope, node.castTag(.StringLiteral).?),
22 .IntegerLiteral => return integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?),
23 .BuiltinCall => return builtinCall(mod, scope, node.castTag(.BuiltinCall).?),
24 .Call => return callExpr(mod, scope, node.castTag(.Call).?),
25 .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),
26 .ControlFlowExpression => return controlFlowExpr(mod, scope, node.castTag(.ControlFlowExpression).?),
27 .If => return ifExpr(mod, scope, node.castTag(.If).?),
28 .Assign => return assign(mod, scope, node.castTag(.Assign).?),
29 .Add => return add(mod, scope, node.castTag(.Add).?),
30 .BangEqual => return cmp(mod, scope, node.castTag(.BangEqual).?, .neq),
31 .EqualEqual => return cmp(mod, scope, node.castTag(.EqualEqual).?, .eq),
32 .GreaterThan => return cmp(mod, scope, node.castTag(.GreaterThan).?, .gt),
33 .GreaterOrEqual => return cmp(mod, scope, node.castTag(.GreaterOrEqual).?, .gte),
34 .LessThan => return cmp(mod, scope, node.castTag(.LessThan).?, .lt),
35 .LessOrEqual => return cmp(mod, scope, node.castTag(.LessOrEqual).?, .lte),
36 .BoolNot => return boolNot(mod, scope, node.castTag(.BoolNot).?),
37 else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}),
38 }
39}
40
41pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) !void {
42 const tracy = trace(@src());
43 defer tracy.end();
44
45 if (block_node.label) |label| {
46 return mod.failTok(parent_scope, label, "TODO implement labeled blocks", .{});
47 }
48
49 var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
50 defer block_arena.deinit();
51
52 var scope = parent_scope;
53 for (block_node.statements()) |statement| {
54 switch (statement.tag) {
55 .VarDecl => {
56 const sub_scope = try block_arena.allocator.create(Scope.LocalVar);
57 const var_decl_node = @fieldParentPtr(ast.Node.VarDecl, "base", statement);
58 sub_scope.* = try varDecl(mod, scope, var_decl_node);
59 scope = &sub_scope.base;
60 },
61 else => _ = try expr(mod, scope, statement),
62 }
63 }
64}
65
66fn varDecl(mod: *Module, scope: *Scope, node: *ast.Node.VarDecl) InnerError!Scope.LocalVar {
67 // TODO implement detection of shadowing
68 if (node.getTrailer("comptime_token")) |comptime_token| {
69 return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
70 }
71 if (node.getTrailer("align_node")) |align_node| {
72 return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});
73 }
74 if (node.getTrailer("type_node")) |type_node| {
75 return mod.failNode(scope, type_node, "TODO implement typed locals", .{});
76 }
77 const tree = scope.tree();
78 switch (tree.token_ids[node.mut_token]) {
79 .Keyword_const => {},
80 .Keyword_var => {
81 return mod.failTok(scope, node.mut_token, "TODO implement mutable locals", .{});
82 },
83 else => unreachable,
84 }
85 // Depending on the type of AST the initialization expression is, we may need an lvalue
86 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
87 // the variable, no memory location needed.
88 const init_node = node.getTrailer("init_node").?;
89 if (nodeNeedsMemoryLocation(init_node)) {
90 return mod.failNode(scope, init_node, "TODO implement result locations", .{});
91 }
92 const init_inst = try expr(mod, scope, init_node);
93 const ident_name = tree.tokenSlice(node.name_token); // TODO support @"aoeu" identifiers
94 return Scope.LocalVar{
95 .parent = scope,
96 .gen_zir = scope.getGenZIR(),
97 .name = ident_name,
98 .inst = init_inst,
99 };
100}
101
102fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
103 const operand = try expr(mod, scope, node.rhs);
104 const tree = scope.tree();
105 const src = tree.token_locs[node.op_token].start;
106 return mod.addZIRInst(scope, src, zir.Inst.BoolNot, .{ .operand = operand }, .{});
107}
108
109fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
110 if (infix_node.lhs.tag == .Identifier) {
111 const ident = @fieldParentPtr(ast.Node.Identifier, "base", infix_node.lhs);
112 const tree = scope.tree();
113 const ident_name = tree.tokenSlice(ident.token);
114 if (std.mem.eql(u8, ident_name, "_")) {
115 return expr(mod, scope, infix_node.rhs);
116 } else {
117 return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
118 }
119 } else {
120 return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
121 }
122}
123
124fn add(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
125 const lhs = try expr(mod, scope, infix_node.lhs);
126 const rhs = try expr(mod, scope, infix_node.rhs);
127
128 const tree = scope.tree();
129 const src = tree.token_locs[infix_node.op_token].start;
130
131 return mod.addZIRInst(scope, src, zir.Inst.Add, .{ .lhs = lhs, .rhs = rhs }, .{});
132}
133
134fn cmp(
135 mod: *Module,
136 scope: *Scope,
137 infix_node: *ast.Node.SimpleInfixOp,
138 op: std.math.CompareOperator,
139) InnerError!*zir.Inst {
140 const lhs = try expr(mod, scope, infix_node.lhs);
141 const rhs = try expr(mod, scope, infix_node.rhs);
142
143 const tree = scope.tree();
144 const src = tree.token_locs[infix_node.op_token].start;
145
146 return mod.addZIRInst(scope, src, zir.Inst.Cmp, .{
147 .lhs = lhs,
148 .op = op,
149 .rhs = rhs,
150 }, .{});
151}
152
153fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.Inst {
154 if (if_node.payload) |payload| {
155 return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for optionals", .{});
156 }
157 if (if_node.@"else") |else_node| {
158 if (else_node.payload) |payload| {
159 return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for error unions", .{});
160 }
161 }
162 var block_scope: Scope.GenZIR = .{
163 .parent = scope,
164 .decl = scope.decl().?,
165 .arena = scope.arena(),
166 .instructions = .{},
167 };
168 defer block_scope.instructions.deinit(mod.gpa);
169
170 const cond = try expr(mod, &block_scope.base, if_node.condition);
171
172 const tree = scope.tree();
173 const if_src = tree.token_locs[if_node.if_token].start;
174 const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
175 .condition = cond,
176 .true_body = undefined, // populated below
177 .false_body = undefined, // populated below
178 }, .{});
179
180 const block = try mod.addZIRInstBlock(scope, if_src, .{
181 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
182 });
183 var then_scope: Scope.GenZIR = .{
184 .parent = scope,
185 .decl = block_scope.decl,
186 .arena = block_scope.arena,
187 .instructions = .{},
188 };
189 defer then_scope.instructions.deinit(mod.gpa);
190
191 const then_result = try expr(mod, &then_scope.base, if_node.body);
192 if (!then_result.tag.isNoReturn()) {
193 const then_src = tree.token_locs[if_node.body.lastToken()].start;
194 _ = try mod.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
195 .block = block,
196 .operand = then_result,
197 }, .{});
198 }
199 condbr.positionals.true_body = .{
200 .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
201 };
202
203 var else_scope: Scope.GenZIR = .{
204 .parent = scope,
205 .decl = block_scope.decl,
206 .arena = block_scope.arena,
207 .instructions = .{},
208 };
209 defer else_scope.instructions.deinit(mod.gpa);
210
211 if (if_node.@"else") |else_node| {
212 const else_result = try expr(mod, &else_scope.base, else_node.body);
213 if (!else_result.tag.isNoReturn()) {
214 const else_src = tree.token_locs[else_node.body.lastToken()].start;
215 _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
216 .block = block,
217 .operand = else_result,
218 }, .{});
219 }
220 } else {
221 // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
222 // by directly allocating the body for this one instruction.
223 const else_src = tree.token_locs[if_node.lastToken()].start;
224 _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.BreakVoid, .{
225 .block = block,
226 }, .{});
227 }
228 condbr.positionals.false_body = .{
229 .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
230 };
231
232 return &block.base;
233}
234
235fn controlFlowExpr(
236 mod: *Module,
237 scope: *Scope,
238 cfe: *ast.Node.ControlFlowExpression,
239) InnerError!*zir.Inst {
240 switch (cfe.kind) {
241 .Break => return mod.failNode(scope, &cfe.base, "TODO implement astgen.Expr for Break", .{}),
242 .Continue => return mod.failNode(scope, &cfe.base, "TODO implement astgen.Expr for Continue", .{}),
243 .Return => {},
244 }
245 const tree = scope.tree();
246 const src = tree.token_locs[cfe.ltoken].start;
247 if (cfe.rhs) |rhs_node| {
248 const operand = try expr(mod, scope, rhs_node);
249 return mod.addZIRInst(scope, src, zir.Inst.Return, .{ .operand = operand }, .{});
250 } else {
251 return mod.addZIRInst(scope, src, zir.Inst.ReturnVoid, .{}, .{});
252 }
253}
254
255fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst {
256 const tracy = trace(@src());
257 defer tracy.end();
258
259 const tree = scope.tree();
260 // TODO implement @"aoeu" identifiers
261 const ident_name = tree.tokenSlice(ident.token);
262 const src = tree.token_locs[ident.token].start;
263 if (mem.eql(u8, ident_name, "_")) {
264 return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
265 }
266
267 if (getSimplePrimitiveValue(ident_name)) |typed_value| {
268 return mod.addZIRInstConst(scope, src, typed_value);
269 }
270
271 if (ident_name.len >= 2) integer: {
272 const first_c = ident_name[0];
273 if (first_c == 'i' or first_c == 'u') {
274 const is_signed = first_c == 'i';
275 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
276 error.Overflow => return mod.failNode(
277 scope,
278 &ident.base,
279 "primitive integer type '{}' exceeds maximum bit width of 65535",
280 .{ident_name},
281 ),
282 error.InvalidCharacter => break :integer,
283 };
284 const val = switch (bit_count) {
285 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
286 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
287 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
288 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
289 else => return mod.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}),
290 };
291 return mod.addZIRInstConst(scope, src, .{
292 .ty = Type.initTag(.type),
293 .val = val,
294 });
295 }
296 }
297
298 // Local variables, including function parameters.
299 {
300 var s = scope;
301 while (true) switch (s.tag) {
302 .local_var => {
303 const local_var = s.cast(Scope.LocalVar).?;
304 if (mem.eql(u8, local_var.name, ident_name)) {
305 return local_var.inst;
306 }
307 s = local_var.parent;
308 },
309 .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
310 else => break,
311 };
312 }
313
314 if (mod.lookupDeclName(scope, ident_name)) |decl| {
315 return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
316 }
317
318 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
319}
320
321fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) InnerError!*zir.Inst {
322 const tree = scope.tree();
323 const unparsed_bytes = tree.tokenSlice(str_lit.token);
324 const arena = scope.arena();
325
326 var bad_index: usize = undefined;
327 const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
328 error.InvalidCharacter => {
329 const bad_byte = unparsed_bytes[bad_index];
330 const src = tree.token_locs[str_lit.token].start;
331 return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
332 },
333 else => |e| return e,
334 };
335
336 const src = tree.token_locs[str_lit.token].start;
337 return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
338}
339
340fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral) InnerError!*zir.Inst {
341 const arena = scope.arena();
342 const tree = scope.tree();
343 const prefixed_bytes = tree.tokenSlice(int_lit.token);
344 const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
345 16
346 else if (mem.startsWith(u8, prefixed_bytes, "0o"))
347 8
348 else if (mem.startsWith(u8, prefixed_bytes, "0b"))
349 2
350 else
351 @as(u8, 10);
352
353 const bytes = if (base == 10)
354 prefixed_bytes
355 else
356 prefixed_bytes[2..];
357
358 if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
359 const int_payload = try arena.create(Value.Payload.Int_u64);
360 int_payload.* = .{ .int = small_int };
361 const src = tree.token_locs[int_lit.token].start;
362 return mod.addZIRInstConst(scope, src, .{
363 .ty = Type.initTag(.comptime_int),
364 .val = Value.initPayload(&int_payload.base),
365 });
366 } else |err| {
367 return mod.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
368 }
369}
370
371fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
372 if (asm_node.outputs.len != 0) {
373 return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
374 }
375 const arena = scope.arena();
376 const tree = scope.tree();
377
378 const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
379 const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
380
381 for (asm_node.inputs) |input, i| {
382 // TODO semantically analyze constraints
383 inputs[i] = try expr(mod, scope, input.constraint);
384 args[i] = try expr(mod, scope, input.expr);
385 }
386
387 const src = tree.token_locs[asm_node.asm_token].start;
388 const return_type = try mod.addZIRInstConst(scope, src, .{
389 .ty = Type.initTag(.type),
390 .val = Value.initTag(.void_type),
391 });
392 const asm_inst = try mod.addZIRInst(scope, src, zir.Inst.Asm, .{
393 .asm_source = try expr(mod, scope, asm_node.template),
394 .return_type = return_type,
395 }, .{
396 .@"volatile" = asm_node.volatile_token != null,
397 //.clobbers = TODO handle clobbers
398 .inputs = inputs,
399 .args = args,
400 });
401 return asm_inst;
402}
403
404fn builtinCall(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
405 const tree = scope.tree();
406 const builtin_name = tree.tokenSlice(call.builtin_token);
407 const src = tree.token_locs[call.builtin_token].start;
408
409 inline for (std.meta.declarations(zir.Inst)) |inst| {
410 if (inst.data != .Type) continue;
411 const T = inst.data.Type;
412 if (!@hasDecl(T, "builtin_name")) continue;
413 if (std.mem.eql(u8, builtin_name, T.builtin_name)) {
414 var value: T = undefined;
415 const positionals = @typeInfo(std.meta.fieldInfo(T, "positionals").field_type).Struct;
416 if (positionals.fields.len == 0) {
417 return mod.addZIRInst(scope, src, T, value.positionals, value.kw_args);
418 }
419 const arg_count: ?usize = if (positionals.fields[0].field_type == []*zir.Inst) null else positionals.fields.len;
420 if (arg_count) |some| {
421 if (call.params_len != some) {
422 return mod.failTok(
423 scope,
424 call.builtin_token,
425 "expected {} parameter{}, found {}",
426 .{ some, if (some == 1) "" else "s", call.params_len },
427 );
428 }
429 const params = call.params();
430 inline for (positionals.fields) |p, i| {
431 @field(value.positionals, p.name) = try expr(mod, scope, params[i]);
432 }
433 } else {
434 return mod.failTok(scope, call.builtin_token, "TODO var args builtin '{}'", .{builtin_name});
435 }
436
437 return mod.addZIRInst(scope, src, T, value.positionals, .{});
438 }
439 }
440 return mod.failTok(scope, call.builtin_token, "TODO implement builtin call for '{}'", .{builtin_name});
441}
442
443fn callExpr(mod: *Module, scope: *Scope, node: *ast.Node.Call) InnerError!*zir.Inst {
444 const tree = scope.tree();
445 const lhs = try expr(mod, scope, node.lhs);
446
447 const param_nodes = node.params();
448 const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
449 for (param_nodes) |param_node, i| {
450 args[i] = try expr(mod, scope, param_node);
451 }
452
453 const src = tree.token_locs[node.lhs.firstToken()].start;
454 return mod.addZIRInst(scope, src, zir.Inst.Call, .{
455 .func = lhs,
456 .args = args,
457 }, .{});
458}
459
460fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.Unreachable) InnerError!*zir.Inst {
461 const tree = scope.tree();
462 const src = tree.token_locs[unreach_node.token].start;
463 return mod.addZIRInst(scope, src, zir.Inst.Unreachable, .{}, .{});
464}
465
466fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
467 const simple_types = std.ComptimeStringMap(Value.Tag, .{
468 .{ "u8", .u8_type },
469 .{ "i8", .i8_type },
470 .{ "isize", .isize_type },
471 .{ "usize", .usize_type },
472 .{ "c_short", .c_short_type },
473 .{ "c_ushort", .c_ushort_type },
474 .{ "c_int", .c_int_type },
475 .{ "c_uint", .c_uint_type },
476 .{ "c_long", .c_long_type },
477 .{ "c_ulong", .c_ulong_type },
478 .{ "c_longlong", .c_longlong_type },
479 .{ "c_ulonglong", .c_ulonglong_type },
480 .{ "c_longdouble", .c_longdouble_type },
481 .{ "f16", .f16_type },
482 .{ "f32", .f32_type },
483 .{ "f64", .f64_type },
484 .{ "f128", .f128_type },
485 .{ "c_void", .c_void_type },
486 .{ "bool", .bool_type },
487 .{ "void", .void_type },
488 .{ "type", .type_type },
489 .{ "anyerror", .anyerror_type },
490 .{ "comptime_int", .comptime_int_type },
491 .{ "comptime_float", .comptime_float_type },
492 .{ "noreturn", .noreturn_type },
493 });
494 if (simple_types.get(name)) |tag| {
495 return TypedValue{
496 .ty = Type.initTag(.type),
497 .val = Value.initTag(tag),
498 };
499 }
500 if (mem.eql(u8, name, "null")) {
501 return TypedValue{
502 .ty = Type.initTag(.@"null"),
503 .val = Value.initTag(.null_value),
504 };
505 }
506 if (mem.eql(u8, name, "undefined")) {
507 return TypedValue{
508 .ty = Type.initTag(.@"undefined"),
509 .val = Value.initTag(.undef),
510 };
511 }
512 if (mem.eql(u8, name, "true")) {
513 return TypedValue{
514 .ty = Type.initTag(.bool),
515 .val = Value.initTag(.bool_true),
516 };
517 }
518 if (mem.eql(u8, name, "false")) {
519 return TypedValue{
520 .ty = Type.initTag(.bool),
521 .val = Value.initTag(.bool_false),
522 };
523 }
524 return null;
525}
526
527fn nodeNeedsMemoryLocation(node: *ast.Node) bool {
528 return switch (node.tag) {
529 .Root,
530 .Use,
531 .TestDecl,
532 .DocComment,
533 .SwitchCase,
534 .SwitchElse,
535 .Else,
536 .Payload,
537 .PointerPayload,
538 .PointerIndexPayload,
539 .ContainerField,
540 .ErrorTag,
541 .FieldInitializer,
542 => unreachable,
543
544 .ControlFlowExpression,
545 .BitNot,
546 .BoolNot,
547 .VarDecl,
548 .Defer,
549 .AddressOf,
550 .OptionalType,
551 .Negation,
552 .NegationWrap,
553 .Resume,
554 .ArrayType,
555 .ArrayTypeSentinel,
556 .PtrType,
557 .SliceType,
558 .Suspend,
559 .AnyType,
560 .ErrorType,
561 .FnProto,
562 .AnyFrameType,
563 .IntegerLiteral,
564 .FloatLiteral,
565 .EnumLiteral,
566 .StringLiteral,
567 .MultilineStringLiteral,
568 .CharLiteral,
569 .BoolLiteral,
570 .NullLiteral,
571 .UndefinedLiteral,
572 .Unreachable,
573 .Identifier,
574 .ErrorSetDecl,
575 .ContainerDecl,
576 .Asm,
577 .Add,
578 .AddWrap,
579 .ArrayCat,
580 .ArrayMult,
581 .Assign,
582 .AssignBitAnd,
583 .AssignBitOr,
584 .AssignBitShiftLeft,
585 .AssignBitShiftRight,
586 .AssignBitXor,
587 .AssignDiv,
588 .AssignSub,
589 .AssignSubWrap,
590 .AssignMod,
591 .AssignAdd,
592 .AssignAddWrap,
593 .AssignMul,
594 .AssignMulWrap,
595 .BangEqual,
596 .BitAnd,
597 .BitOr,
598 .BitShiftLeft,
599 .BitShiftRight,
600 .BitXor,
601 .BoolAnd,
602 .BoolOr,
603 .Div,
604 .EqualEqual,
605 .ErrorUnion,
606 .GreaterOrEqual,
607 .GreaterThan,
608 .LessOrEqual,
609 .LessThan,
610 .MergeErrorSets,
611 .Mod,
612 .Mul,
613 .MulWrap,
614 .Range,
615 .Period,
616 .Sub,
617 .SubWrap,
618 => false,
619
620 .ArrayInitializer,
621 .ArrayInitializerDot,
622 .StructInitializer,
623 .StructInitializerDot,
624 => true,
625
626 .GroupedExpression => nodeNeedsMemoryLocation(node.castTag(.GroupedExpression).?.expr),
627
628 .UnwrapOptional => @panic("TODO nodeNeedsMemoryLocation for UnwrapOptional"),
629 .Catch => @panic("TODO nodeNeedsMemoryLocation for Catch"),
630 .Await => @panic("TODO nodeNeedsMemoryLocation for Await"),
631 .Try => @panic("TODO nodeNeedsMemoryLocation for Try"),
632 .If => @panic("TODO nodeNeedsMemoryLocation for If"),
633 .SuffixOp => @panic("TODO nodeNeedsMemoryLocation for SuffixOp"),
634 .Call => @panic("TODO nodeNeedsMemoryLocation for Call"),
635 .Switch => @panic("TODO nodeNeedsMemoryLocation for Switch"),
636 .While => @panic("TODO nodeNeedsMemoryLocation for While"),
637 .For => @panic("TODO nodeNeedsMemoryLocation for For"),
638 .BuiltinCall => @panic("TODO nodeNeedsMemoryLocation for BuiltinCall"),
639 .Comptime => @panic("TODO nodeNeedsMemoryLocation for Comptime"),
640 .Nosuspend => @panic("TODO nodeNeedsMemoryLocation for Nosuspend"),
641 .Block => @panic("TODO nodeNeedsMemoryLocation for Block"),
642 };
643}
src-self-hosted/cbe.h created+8
...@@ -0,0 +1,8 @@
1#if __STDC_VERSION__ >= 201112L
2#define noreturn _Noreturn
3#elif __GNUC__ && !__STRICT_ANSI__
4#define noreturn __attribute__ ((noreturn))
5#else
6#define noreturn
7#endif
8
src-self-hosted/codegen.zig+820-178
...@@ -10,6 +10,19 @@ const Module = @import("Module.zig");...@@ -10,6 +10,19 @@ const Module = @import("Module.zig");
10const ErrorMsg = Module.ErrorMsg;10const ErrorMsg = Module.ErrorMsg;
11const Target = std.Target;11const Target = std.Target;
12const Allocator = mem.Allocator;12const Allocator = mem.Allocator;
13const trace = @import("tracy.zig").trace;
14
15/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
16pub const BlockData = struct {
17 relocs: std.ArrayListUnmanaged(Reloc) = .{},
18};
19
20pub const Reloc = union(enum) {
21 /// The value is an offset into the `Function` `code` from the beginning.
22 /// To perform the reloc, write 32-bit signed little-endian integer
23 /// which is a relative jump, based on the address following the reloc.
24 rel32: usize,
25};
1326
14pub const Result = union(enum) {27pub const Result = union(enum) {
15 /// The `code` parameter passed to `generateSymbol` has the value appended.28 /// The `code` parameter passed to `generateSymbol` has the value appended.
...@@ -20,7 +33,7 @@ pub const Result = union(enum) {...@@ -20,7 +33,7 @@ pub const Result = union(enum) {
20};33};
2134
22pub fn generateSymbol(35pub fn generateSymbol(
23 bin_file: *link.ElfFile,36 bin_file: *link.File.Elf,
24 src: usize,37 src: usize,
25 typed_value: TypedValue,38 typed_value: TypedValue,
26 code: *std.ArrayList(u8),39 code: *std.ArrayList(u8),
...@@ -29,27 +42,52 @@ pub fn generateSymbol(...@@ -29,27 +42,52 @@ pub fn generateSymbol(
29 /// A Decl that this symbol depends on had a semantic analysis failure.42 /// A Decl that this symbol depends on had a semantic analysis failure.
30 AnalysisFail,43 AnalysisFail,
31}!Result {44}!Result {
45 const tracy = trace(@src());
46 defer tracy.end();
47
32 switch (typed_value.ty.zigTypeTag()) {48 switch (typed_value.ty.zigTypeTag()) {
33 .Fn => {49 .Fn => {
34 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;50 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
3551
52 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
53 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
54 defer bin_file.allocator.free(param_types);
55 fn_type.fnParamTypes(param_types);
56 var mc_args = try bin_file.allocator.alloc(MCValue, param_types.len);
57 defer bin_file.allocator.free(mc_args);
58
59 var branch_stack = std.ArrayList(Function.Branch).init(bin_file.allocator);
60 defer {
61 assert(branch_stack.items.len == 1);
62 branch_stack.items[0].deinit(bin_file.allocator);
63 branch_stack.deinit();
64 }
65 const branch = try branch_stack.addOne();
66 branch.* = .{};
67
36 var function = Function{68 var function = Function{
69 .gpa = bin_file.allocator,
37 .target = &bin_file.options.target,70 .target = &bin_file.options.target,
38 .bin_file = bin_file,71 .bin_file = bin_file,
39 .mod_fn = module_fn,72 .mod_fn = module_fn,
40 .code = code,73 .code = code,
41 .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(bin_file.allocator),
42 .err_msg = null,74 .err_msg = null,
75 .args = mc_args,
76 .arg_index = 0,
77 .branch_stack = &branch_stack,
78 .src = src,
43 };79 };
44 defer function.inst_table.deinit();
4580
46 for (module_fn.analysis.success.instructions) |inst| {81 const cc = fn_type.fnCallingConvention();
47 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {82 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {
48 error.CodegenFail => return Result{ .fail = function.err_msg.? },83 error.CodegenFail => return Result{ .fail = function.err_msg.? },
49 else => |e| return e,84 else => |e| return e,
50 };85 };
51 try function.inst_table.putNoClobber(inst, new_inst);86
52 }87 function.gen() catch |err| switch (err) {
88 error.CodegenFail => return Result{ .fail = function.err_msg.? },
89 else => |e| return e,
90 };
5391
54 if (function.err_msg) |em| {92 if (function.err_msg) |em| {
55 return Result{ .fail = em };93 return Result{ .fail = em };
...@@ -146,47 +184,434 @@ pub fn generateSymbol(...@@ -146,47 +184,434 @@ pub fn generateSymbol(
146 }184 }
147}185}
148186
187const InnerError = error{
188 OutOfMemory,
189 CodegenFail,
190};
191
192const MCValue = union(enum) {
193 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
194 none,
195 /// Control flow will not allow this value to be observed.
196 unreach,
197 /// No more references to this value remain.
198 dead,
199 /// A pointer-sized integer that fits in a register.
200 immediate: u64,
201 /// The constant was emitted into the code, at this offset.
202 embedded_in_code: usize,
203 /// The value is in a target-specific register. The value can
204 /// be @intToEnum casted to the respective Reg enum.
205 register: usize,
206 /// The value is in memory at a hard-coded address.
207 memory: u64,
208 /// The value is one of the stack variables.
209 stack_offset: u64,
210 /// The value is in the compare flags assuming an unsigned operation,
211 /// with this operator applied on top of it.
212 compare_flags_unsigned: std.math.CompareOperator,
213 /// The value is in the compare flags assuming a signed operation,
214 /// with this operator applied on top of it.
215 compare_flags_signed: std.math.CompareOperator,
216
217 fn isMemory(mcv: MCValue) bool {
218 return switch (mcv) {
219 .embedded_in_code, .memory, .stack_offset => true,
220 else => false,
221 };
222 }
223
224 fn isImmediate(mcv: MCValue) bool {
225 return switch (mcv) {
226 .immediate => true,
227 else => false,
228 };
229 }
230
231 fn isMutable(mcv: MCValue) bool {
232 return switch (mcv) {
233 .none => unreachable,
234 .unreach => unreachable,
235 .dead => unreachable,
236
237 .immediate,
238 .embedded_in_code,
239 .memory,
240 .compare_flags_unsigned,
241 .compare_flags_signed,
242 => false,
243
244 .register,
245 .stack_offset,
246 => true,
247 };
248 }
249};
250
149const Function = struct {251const Function = struct {
150 bin_file: *link.ElfFile,252 gpa: *Allocator,
253 bin_file: *link.File.Elf,
151 target: *const std.Target,254 target: *const std.Target,
152 mod_fn: *const Module.Fn,255 mod_fn: *const Module.Fn,
153 code: *std.ArrayList(u8),256 code: *std.ArrayList(u8),
154 inst_table: std.AutoHashMap(*ir.Inst, MCValue),
155 err_msg: ?*ErrorMsg,257 err_msg: ?*ErrorMsg,
258 args: []MCValue,
259 arg_index: usize,
260 src: usize,
156261
157 const MCValue = union(enum) {262 /// Whenever there is a runtime branch, we push a Branch onto this stack,
158 none,263 /// and pop it off when the runtime branch joins. This provides an "overlay"
159 unreach,264 /// of the table of mappings from instructions to `MCValue` from within the branch.
160 /// A pointer-sized integer that fits in a register.265 /// This way we can modify the `MCValue` for an instruction in different ways
161 immediate: u64,266 /// within different branches. Special consideration is needed when a branch
162 /// The constant was emitted into the code, at this offset.267 /// joins with its parent, to make sure all instructions have the same MCValue
163 embedded_in_code: usize,268 /// across each runtime branch upon joining.
164 /// The value is in a target-specific register. The value can269 branch_stack: *std.ArrayList(Branch),
165 /// be @intToEnum casted to the respective Reg enum.270
166 register: usize,271 const Branch = struct {
167 /// The value is in memory at a hard-coded address.272 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
168 memory: u64,273
274 /// The key is an enum value of an arch-specific register.
275 registers: std.AutoHashMapUnmanaged(usize, RegisterAllocation) = .{},
276
277 /// Maps offset to what is stored there.
278 stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{},
279 /// Offset from the stack base, representing the end of the stack frame.
280 max_end_stack: u32 = 0,
281 /// Represents the current end stack offset. If there is no existing slot
282 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
283 next_stack_offset: u32 = 0,
284
285 fn deinit(self: *Branch, gpa: *Allocator) void {
286 self.inst_table.deinit(gpa);
287 self.registers.deinit(gpa);
288 self.stack.deinit(gpa);
289 self.* = undefined;
290 }
291 };
292
293 const RegisterAllocation = struct {
294 inst: *ir.Inst,
169 };295 };
170296
171 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {297 const StackAllocation = struct {
298 inst: *ir.Inst,
299 size: u32,
300 };
301
302 fn gen(self: *Function) !void {
303 switch (self.target.cpu.arch) {
304 .arm => return self.genArch(.arm),
305 .armeb => return self.genArch(.armeb),
306 .aarch64 => return self.genArch(.aarch64),
307 .aarch64_be => return self.genArch(.aarch64_be),
308 .aarch64_32 => return self.genArch(.aarch64_32),
309 .arc => return self.genArch(.arc),
310 .avr => return self.genArch(.avr),
311 .bpfel => return self.genArch(.bpfel),
312 .bpfeb => return self.genArch(.bpfeb),
313 .hexagon => return self.genArch(.hexagon),
314 .mips => return self.genArch(.mips),
315 .mipsel => return self.genArch(.mipsel),
316 .mips64 => return self.genArch(.mips64),
317 .mips64el => return self.genArch(.mips64el),
318 .msp430 => return self.genArch(.msp430),
319 .powerpc => return self.genArch(.powerpc),
320 .powerpc64 => return self.genArch(.powerpc64),
321 .powerpc64le => return self.genArch(.powerpc64le),
322 .r600 => return self.genArch(.r600),
323 .amdgcn => return self.genArch(.amdgcn),
324 .riscv32 => return self.genArch(.riscv32),
325 .riscv64 => return self.genArch(.riscv64),
326 .sparc => return self.genArch(.sparc),
327 .sparcv9 => return self.genArch(.sparcv9),
328 .sparcel => return self.genArch(.sparcel),
329 .s390x => return self.genArch(.s390x),
330 .tce => return self.genArch(.tce),
331 .tcele => return self.genArch(.tcele),
332 .thumb => return self.genArch(.thumb),
333 .thumbeb => return self.genArch(.thumbeb),
334 .i386 => return self.genArch(.i386),
335 .x86_64 => return self.genArch(.x86_64),
336 .xcore => return self.genArch(.xcore),
337 .nvptx => return self.genArch(.nvptx),
338 .nvptx64 => return self.genArch(.nvptx64),
339 .le32 => return self.genArch(.le32),
340 .le64 => return self.genArch(.le64),
341 .amdil => return self.genArch(.amdil),
342 .amdil64 => return self.genArch(.amdil64),
343 .hsail => return self.genArch(.hsail),
344 .hsail64 => return self.genArch(.hsail64),
345 .spir => return self.genArch(.spir),
346 .spir64 => return self.genArch(.spir64),
347 .kalimba => return self.genArch(.kalimba),
348 .shave => return self.genArch(.shave),
349 .lanai => return self.genArch(.lanai),
350 .wasm32 => return self.genArch(.wasm32),
351 .wasm64 => return self.genArch(.wasm64),
352 .renderscript32 => return self.genArch(.renderscript32),
353 .renderscript64 => return self.genArch(.renderscript64),
354 .ve => return self.genArch(.ve),
355 }
356 }
357
358 fn genArch(self: *Function, comptime arch: std.Target.Cpu.Arch) !void {
359 try self.code.ensureCapacity(self.code.items.len + 11);
360
361 // push rbp
362 // mov rbp, rsp
363 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });
364
365 // sub rsp, x
366 const stack_end = self.branch_stack.items[0].max_end_stack;
367 if (stack_end > std.math.maxInt(i32)) {
368 return self.fail(self.src, "too much stack used in call parameters", .{});
369 } else if (stack_end > std.math.maxInt(i8)) {
370 // 48 83 ec xx sub rsp,0x10
371 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec });
372 const x = @intCast(u32, stack_end);
373 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
374 } else if (stack_end != 0) {
375 // 48 81 ec xx xx xx xx sub rsp,0x80
376 const x = @intCast(u8, stack_end);
377 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x });
378 }
379
380 try self.genBody(self.mod_fn.analysis.success, arch);
381 }
382
383 fn genBody(self: *Function, body: ir.Body, comptime arch: std.Target.Cpu.Arch) InnerError!void {
384 const inst_table = &self.branch_stack.items[0].inst_table;
385 for (body.instructions) |inst| {
386 const new_inst = try self.genFuncInst(inst, arch);
387 try inst_table.putNoClobber(self.gpa, inst, new_inst);
388 }
389 }
390
391 fn genFuncInst(self: *Function, inst: *ir.Inst, comptime arch: std.Target.Cpu.Arch) !MCValue {
172 switch (inst.tag) {392 switch (inst.tag) {
173 .breakpoint => return self.genBreakpoint(inst.src),393 .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch),
174 .call => return self.genCall(inst.cast(ir.Inst.Call).?),394 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),
175 .unreach => return MCValue{ .unreach = {} },395 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch),
396 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
397 .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch),
398 .br => return self.genBr(inst.cast(ir.Inst.Br).?, arch),
399 .breakpoint => return self.genBreakpoint(inst.src, arch),
400 .brvoid => return self.genBrVoid(inst.cast(ir.Inst.BrVoid).?, arch),
401 .call => return self.genCall(inst.cast(ir.Inst.Call).?, arch),
402 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch),
403 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?, arch),
176 .constant => unreachable, // excluded from function bodies404 .constant => unreachable, // excluded from function bodies
177 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),405 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?, arch),
406 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?, arch),
178 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),407 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
179 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),408 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?, arch),
180 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?),409 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),
181 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),410 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?, arch),
182 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),411 .unreach => return MCValue{ .unreach = {} },
183 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),412 .not => return self.genNot(inst.cast(ir.Inst.Not).?, arch),
184 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?),
185 }413 }
186 }414 }
187415
188 fn genBreakpoint(self: *Function, src: usize) !MCValue {416 fn genNot(self: *Function, inst: *ir.Inst.Not, comptime arch: std.Target.Cpu.Arch) !MCValue {
189 switch (self.target.cpu.arch) {417 // No side effects, so if it's unreferenced, do nothing.
418 if (inst.base.isUnused())
419 return MCValue.dead;
420 const operand = try self.resolveInst(inst.args.operand);
421 switch (operand) {
422 .dead => unreachable,
423 .unreach => unreachable,
424 .compare_flags_unsigned => |op| return MCValue{
425 .compare_flags_unsigned = switch (op) {
426 .gte => .lt,
427 .gt => .lte,
428 .neq => .eq,
429 .lt => .gte,
430 .lte => .gt,
431 .eq => .neq,
432 },
433 },
434 .compare_flags_signed => |op| return MCValue{
435 .compare_flags_signed = switch (op) {
436 .gte => .lt,
437 .gt => .lte,
438 .neq => .eq,
439 .lt => .gte,
440 .lte => .gt,
441 .eq => .neq,
442 },
443 },
444 else => {},
445 }
446
447 switch (arch) {
448 .x86_64 => {
449 var imm = ir.Inst.Constant{
450 .base = .{
451 .tag = .constant,
452 .deaths = 0,
453 .ty = inst.args.operand.ty,
454 .src = inst.args.operand.src,
455 },
456 .val = Value.initTag(.bool_true),
457 };
458 return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30);
459 },
460 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
461 }
462 }
463
464 fn genAdd(self: *Function, inst: *ir.Inst.Add, comptime arch: std.Target.Cpu.Arch) !MCValue {
465 // No side effects, so if it's unreferenced, do nothing.
466 if (inst.base.isUnused())
467 return MCValue.dead;
468 switch (arch) {
469 .x86_64 => {
470 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 0, 0x00);
471 },
472 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
473 }
474 }
475
476 fn genSub(self: *Function, inst: *ir.Inst.Sub, comptime arch: std.Target.Cpu.Arch) !MCValue {
477 // No side effects, so if it's unreferenced, do nothing.
478 if (inst.base.isUnused())
479 return MCValue.dead;
480 switch (arch) {
481 .x86_64 => {
482 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 5, 0x28);
483 },
484 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
485 }
486 }
487
488 /// ADD, SUB, XOR, OR, AND
489 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
490 try self.code.ensureCapacity(self.code.items.len + 8);
491
492 const lhs = try self.resolveInst(op_lhs);
493 const rhs = try self.resolveInst(op_rhs);
494
495 // There are 2 operands, destination and source.
496 // Either one, but not both, can be a memory operand.
497 // Source operand can be an immediate, 8 bits or 32 bits.
498 // So, if either one of the operands dies with this instruction, we can use it
499 // as the result MCValue.
500 var dst_mcv: MCValue = undefined;
501 var src_mcv: MCValue = undefined;
502 var src_inst: *ir.Inst = undefined;
503 if (inst.operandDies(0) and lhs.isMutable()) {
504 // LHS dies; use it as the destination.
505 // Both operands cannot be memory.
506 src_inst = op_rhs;
507 if (lhs.isMemory() and rhs.isMemory()) {
508 dst_mcv = try self.copyToNewRegister(op_lhs);
509 src_mcv = rhs;
510 } else {
511 dst_mcv = lhs;
512 src_mcv = rhs;
513 }
514 } else if (inst.operandDies(1) and rhs.isMutable()) {
515 // RHS dies; use it as the destination.
516 // Both operands cannot be memory.
517 src_inst = op_lhs;
518 if (lhs.isMemory() and rhs.isMemory()) {
519 dst_mcv = try self.copyToNewRegister(op_rhs);
520 src_mcv = lhs;
521 } else {
522 dst_mcv = rhs;
523 src_mcv = lhs;
524 }
525 } else {
526 if (lhs.isMemory()) {
527 dst_mcv = try self.copyToNewRegister(op_lhs);
528 src_mcv = rhs;
529 src_inst = op_rhs;
530 } else {
531 dst_mcv = try self.copyToNewRegister(op_rhs);
532 src_mcv = lhs;
533 src_inst = op_lhs;
534 }
535 }
536 // This instruction supports only signed 32-bit immediates at most. If the immediate
537 // value is larger than this, we put it in a register.
538 // A potential opportunity for future optimization here would be keeping track
539 // of the fact that the instruction is available both as an immediate
540 // and as a register.
541 switch (src_mcv) {
542 .immediate => |imm| {
543 if (imm > std.math.maxInt(u31)) {
544 src_mcv = try self.copyToNewRegister(src_inst);
545 }
546 },
547 else => {},
548 }
549
550 try self.genX8664BinMathCode(inst.src, dst_mcv, src_mcv, opx, mr);
551
552 return dst_mcv;
553 }
554
555 fn genX8664BinMathCode(self: *Function, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
556 switch (dst_mcv) {
557 .none => unreachable,
558 .dead, .unreach, .immediate => unreachable,
559 .compare_flags_unsigned => unreachable,
560 .compare_flags_signed => unreachable,
561 .register => |dst_reg_usize| {
562 const dst_reg = @intToEnum(Reg(.x86_64), @intCast(u8, dst_reg_usize));
563 switch (src_mcv) {
564 .none => unreachable,
565 .dead, .unreach => unreachable,
566 .register => |src_reg_usize| {
567 const src_reg = @intToEnum(Reg(.x86_64), @intCast(u8, src_reg_usize));
568 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
569 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
570 },
571 .immediate => |imm| {
572 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
573 // 81 /opx id
574 if (imm32 <= std.math.maxInt(u7)) {
575 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
576 self.code.appendSliceAssumeCapacity(&[_]u8{
577 0x83,
578 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
579 @intCast(u8, imm32),
580 });
581 } else {
582 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
583 self.code.appendSliceAssumeCapacity(&[_]u8{
584 0x81,
585 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
586 });
587 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
588 }
589 },
590 .embedded_in_code, .memory, .stack_offset => {
591 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
592 },
593 .compare_flags_unsigned => {
594 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
595 },
596 .compare_flags_signed => {
597 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
598 },
599 }
600 },
601 .embedded_in_code, .memory, .stack_offset => {
602 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
603 },
604 }
605 }
606
607 fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue {
608 const i = self.arg_index;
609 self.arg_index += 1;
610 return self.args[i];
611 }
612
613 fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue {
614 switch (arch) {
190 .i386, .x86_64 => {615 .i386, .x86_64 => {
191 try self.code.append(0xcc); // int3616 try self.code.append(0xcc); // int3
192 },617 },
...@@ -195,14 +620,43 @@ const Function = struct {...@@ -195,14 +620,43 @@ const Function = struct {
195 return .none;620 return .none;
196 }621 }
197622
198 fn genCall(self: *Function, inst: *ir.Inst.Call) !MCValue {623 fn genCall(self: *Function, inst: *ir.Inst.Call, comptime arch: std.Target.Cpu.Arch) !MCValue {
199 switch (self.target.cpu.arch) {624 const fn_ty = inst.args.func.ty;
200 .x86_64, .i386 => {625 const cc = fn_ty.fnCallingConvention();
201 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {626 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
202 if (inst.args.args.len != 0) {627 defer self.gpa.free(param_types);
203 return self.fail(inst.base.src, "TODO implement call with more than 0 parameters", .{});628 fn_ty.fnParamTypes(param_types);
629 var mc_args = try self.gpa.alloc(MCValue, param_types.len);
630 defer self.gpa.free(mc_args);
631 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);
632
633 switch (arch) {
634 .x86_64 => {
635 for (mc_args) |mc_arg, arg_i| {
636 const arg = inst.args.args[arg_i];
637 const arg_mcv = try self.resolveInst(inst.args.args[arg_i]);
638 switch (mc_arg) {
639 .none => continue,
640 .register => |reg| {
641 try self.genSetReg(arg.src, arch, @intToEnum(Reg(arch), @intCast(u8, reg)), arg_mcv);
642 // TODO interact with the register allocator to mark the instruction as moved.
643 },
644 .stack_offset => {
645 // Here we need to emit instructions like this:
646 // mov qword ptr [rsp + stack_offset], x
647 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
648 },
649 .immediate => unreachable,
650 .unreach => unreachable,
651 .dead => unreachable,
652 .embedded_in_code => unreachable,
653 .memory => unreachable,
654 .compare_flags_signed => unreachable,
655 .compare_flags_unsigned => unreachable,
204 }656 }
657 }
205658
659 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {
206 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {660 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
207 const func = func_val.func;661 const func = func_val.func;
208 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];662 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
...@@ -210,17 +664,11 @@ const Function = struct {...@@ -210,17 +664,11 @@ const Function = struct {
210 const ptr_bytes: u64 = @divExact(ptr_bits, 8);664 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
211 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);665 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
212 // ff 14 25 xx xx xx xx call [addr]666 // ff 14 25 xx xx xx xx call [addr]
213 try self.code.resize(self.code.items.len + 7);667 try self.code.ensureCapacity(self.code.items.len + 7);
214 self.code.items[self.code.items.len - 7 ..][0..3].* = [3]u8{ 0xff, 0x14, 0x25 };668 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
215 mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], got_addr);669 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
216 const return_type = func.fn_type.fnReturnType();
217 switch (return_type.zigTypeTag()) {
218 .Void => return MCValue{ .none = {} },
219 .NoReturn => return MCValue{ .unreach = {} },
220 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
221 }
222 } else {670 } else {
223 return self.fail(inst.base.src, "TODO implement calling weird function values", .{});671 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
224 }672 }
225 } else {673 } else {
226 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});674 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
...@@ -228,121 +676,210 @@ const Function = struct {...@@ -228,121 +676,210 @@ const Function = struct {
228 },676 },
229 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),677 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
230 }678 }
679
680 const return_type = fn_ty.fnReturnType();
681 switch (return_type.zigTypeTag()) {
682 .Void => return MCValue{ .none = {} },
683 .NoReturn => return MCValue{ .unreach = {} },
684 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
685 }
231 }686 }
232687
233 fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {688 fn ret(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, mcv: MCValue) !MCValue {
234 switch (self.target.cpu.arch) {689 if (mcv != .none) {
235 .i386, .x86_64 => {690 return self.fail(src, "TODO implement return with non-void operand", .{});
691 }
692 switch (arch) {
693 .i386 => {
236 try self.code.append(0xc3); // ret694 try self.code.append(0xc3); // ret
237 },695 },
238 else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.target.cpu.arch}),696 .x86_64 => {
697 try self.code.appendSlice(&[_]u8{
698 0x5d, // pop rbp
699 0xc3, // ret
700 });
701 },
702 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
239 }703 }
240 return .unreach;704 return .unreach;
241 }705 }
242706
243 fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue {707 fn genRet(self: *Function, inst: *ir.Inst.Ret, comptime arch: std.Target.Cpu.Arch) !MCValue {
244 switch (self.target.cpu.arch) {708 const operand = try self.resolveInst(inst.args.operand);
709 return self.ret(inst.base.src, arch, operand);
710 }
711
712 fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {
713 return self.ret(inst.base.src, arch, .none);
714 }
715
716 fn genCmp(self: *Function, inst: *ir.Inst.Cmp, comptime arch: std.Target.Cpu.Arch) !MCValue {
717 // No side effects, so if it's unreferenced, do nothing.
718 if (inst.base.isUnused())
719 return MCValue.dead;
720 switch (arch) {
721 .x86_64 => {
722 try self.code.ensureCapacity(self.code.items.len + 8);
723
724 const lhs = try self.resolveInst(inst.args.lhs);
725 const rhs = try self.resolveInst(inst.args.rhs);
726
727 // There are 2 operands, destination and source.
728 // Either one, but not both, can be a memory operand.
729 // Source operand can be an immediate, 8 bits or 32 bits.
730 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
731 try self.copyToNewRegister(inst.args.lhs)
732 else
733 lhs;
734 // This instruction supports only signed 32-bit immediates at most.
735 const src_mcv = try self.limitImmediateType(inst.args.rhs, i32);
736
737 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);
738 const info = inst.args.lhs.ty.intInfo(self.target.*);
739 if (info.signed) {
740 return MCValue{ .compare_flags_signed = inst.args.op };
741 } else {
742 return MCValue{ .compare_flags_unsigned = inst.args.op };
743 }
744 },
245 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),745 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
246 }746 }
247 }747 }
248748
249 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue {749 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
250 switch (self.target.cpu.arch) {750 switch (arch) {
751 .x86_64 => {
752 try self.code.ensureCapacity(self.code.items.len + 6);
753
754 const cond = try self.resolveInst(inst.args.condition);
755 switch (cond) {
756 .compare_flags_signed => |cmp_op| {
757 // Here we map to the opposite opcode because the jump is to the false branch.
758 const opcode: u8 = switch (cmp_op) {
759 .gte => 0x8c,
760 .gt => 0x8e,
761 .neq => 0x84,
762 .lt => 0x8d,
763 .lte => 0x8f,
764 .eq => 0x85,
765 };
766 return self.genX86CondBr(inst, opcode, arch);
767 },
768 .compare_flags_unsigned => |cmp_op| {
769 // Here we map to the opposite opcode because the jump is to the false branch.
770 const opcode: u8 = switch (cmp_op) {
771 .gte => 0x82,
772 .gt => 0x86,
773 .neq => 0x84,
774 .lt => 0x83,
775 .lte => 0x87,
776 .eq => 0x85,
777 };
778 return self.genX86CondBr(inst, opcode, arch);
779 },
780 .register => |reg_usize| {
781 const reg = @intToEnum(Reg(arch), @intCast(u8, reg_usize));
782 // test reg, 1
783 // TODO detect al, ax, eax
784 try self.code.ensureCapacity(self.code.items.len + 4);
785 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
786 self.code.appendSliceAssumeCapacity(&[_]u8{
787 0xf6,
788 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
789 0x01,
790 });
791 return self.genX86CondBr(inst, 0x84, arch);
792 },
793 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
794 }
795 },
251 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),796 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
252 }797 }
253 }798 }
254799
255 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue {800 fn genX86CondBr(self: *Function, inst: *ir.Inst.CondBr, opcode: u8, comptime arch: std.Target.Cpu.Arch) !MCValue {
256 switch (self.target.cpu.arch) {801 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
802 const reloc = Reloc{ .rel32 = self.code.items.len };
803 self.code.items.len += 4;
804 try self.genBody(inst.args.true_body, arch);
805 try self.performReloc(inst.base.src, reloc);
806 try self.genBody(inst.args.false_body, arch);
807 return MCValue.unreach;
808 }
809
810 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull, comptime arch: std.Target.Cpu.Arch) !MCValue {
811 switch (arch) {
257 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),812 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
258 }813 }
259 }814 }
260815
261 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue {816 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull, comptime arch: std.Target.Cpu.Arch) !MCValue {
262 // Here you can specialize this instruction if it makes sense to, otherwise the default817 // Here you can specialize this instruction if it makes sense to, otherwise the default
263 // will call genIsNull and invert the result.818 // will call genIsNull and invert the result.
264 switch (self.target.cpu.arch) {819 switch (arch) {
265 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),820 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
266 }821 }
267 }822 }
268823
269 fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void {824 fn genBlock(self: *Function, inst: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {
270 switch (self.target.cpu.arch) {825 if (inst.base.ty.hasCodeGenBits()) {
826 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});
827 }
828 // A block is nothing but a setup to be able to jump to the end.
829 defer inst.codegen.relocs.deinit(self.gpa);
830 try self.genBody(inst.args.body, arch);
831
832 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
833
834 return MCValue.none;
835 }
836
837 fn performReloc(self: *Function, src: usize, reloc: Reloc) !void {
838 switch (reloc) {
839 .rel32 => |pos| {
840 const amt = self.code.items.len - (pos + 4);
841 const s32_amt = std.math.cast(i32, amt) catch
842 return self.fail(src, "unable to perform relocation: jump too far", .{});
843 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
844 },
845 }
846 }
847
848 fn genBr(self: *Function, inst: *ir.Inst.Br, comptime arch: std.Target.Cpu.Arch) !MCValue {
849 if (!inst.args.operand.ty.hasCodeGenBits())
850 return self.brVoid(inst.base.src, inst.args.block, arch);
851
852 const operand = try self.resolveInst(inst.args.operand);
853 switch (arch) {
854 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
855 }
856 }
857
858 fn genBrVoid(self: *Function, inst: *ir.Inst.BrVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {
859 return self.brVoid(inst.base.src, inst.args.block, arch);
860 }
861
862 fn brVoid(self: *Function, src: usize, block: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {
863 // Emit a jump with a relocation. It will be patched up after the block ends.
864 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
865
866 switch (arch) {
271 .i386, .x86_64 => {867 .i386, .x86_64 => {
272 // TODO x86 treats the operands as signed868 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
273 if (amount <= std.math.maxInt(u8)) {869 // which is available if the jump is 127 bytes or less forward.
274 try self.code.resize(self.code.items.len + 2);870 try self.code.resize(self.code.items.len + 5);
275 self.code.items[self.code.items.len - 2] = 0xeb;871 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
276 self.code.items[self.code.items.len - 1] = @intCast(u8, amount);872 // Leave the jump offset undefined
277 } else {873 block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 });
278 try self.code.resize(self.code.items.len + 5);
279 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
280 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
281 mem.writeIntLittle(u32, imm_ptr, amount);
282 }
283 },874 },
284 else => return self.fail(src, "TODO implement relative forward jump for {}", .{self.target.cpu.arch}),875 else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),
285 }876 }
877 return .none;
286 }878 }
287879
288 fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue {880 fn genAsm(self: *Function, inst: *ir.Inst.Assembly, comptime arch: Target.Cpu.Arch) !MCValue {
289 // TODO convert to inline function881 if (!inst.args.is_volatile and inst.base.isUnused())
290 switch (self.target.cpu.arch) {882 return MCValue.dead;
291 .arm => return self.genAsmArch(.arm, inst),
292 .armeb => return self.genAsmArch(.armeb, inst),
293 .aarch64 => return self.genAsmArch(.aarch64, inst),
294 .aarch64_be => return self.genAsmArch(.aarch64_be, inst),
295 .aarch64_32 => return self.genAsmArch(.aarch64_32, inst),
296 .arc => return self.genAsmArch(.arc, inst),
297 .avr => return self.genAsmArch(.avr, inst),
298 .bpfel => return self.genAsmArch(.bpfel, inst),
299 .bpfeb => return self.genAsmArch(.bpfeb, inst),
300 .hexagon => return self.genAsmArch(.hexagon, inst),
301 .mips => return self.genAsmArch(.mips, inst),
302 .mipsel => return self.genAsmArch(.mipsel, inst),
303 .mips64 => return self.genAsmArch(.mips64, inst),
304 .mips64el => return self.genAsmArch(.mips64el, inst),
305 .msp430 => return self.genAsmArch(.msp430, inst),
306 .powerpc => return self.genAsmArch(.powerpc, inst),
307 .powerpc64 => return self.genAsmArch(.powerpc64, inst),
308 .powerpc64le => return self.genAsmArch(.powerpc64le, inst),
309 .r600 => return self.genAsmArch(.r600, inst),
310 .amdgcn => return self.genAsmArch(.amdgcn, inst),
311 .riscv32 => return self.genAsmArch(.riscv32, inst),
312 .riscv64 => return self.genAsmArch(.riscv64, inst),
313 .sparc => return self.genAsmArch(.sparc, inst),
314 .sparcv9 => return self.genAsmArch(.sparcv9, inst),
315 .sparcel => return self.genAsmArch(.sparcel, inst),
316 .s390x => return self.genAsmArch(.s390x, inst),
317 .tce => return self.genAsmArch(.tce, inst),
318 .tcele => return self.genAsmArch(.tcele, inst),
319 .thumb => return self.genAsmArch(.thumb, inst),
320 .thumbeb => return self.genAsmArch(.thumbeb, inst),
321 .i386 => return self.genAsmArch(.i386, inst),
322 .x86_64 => return self.genAsmArch(.x86_64, inst),
323 .xcore => return self.genAsmArch(.xcore, inst),
324 .nvptx => return self.genAsmArch(.nvptx, inst),
325 .nvptx64 => return self.genAsmArch(.nvptx64, inst),
326 .le32 => return self.genAsmArch(.le32, inst),
327 .le64 => return self.genAsmArch(.le64, inst),
328 .amdil => return self.genAsmArch(.amdil, inst),
329 .amdil64 => return self.genAsmArch(.amdil64, inst),
330 .hsail => return self.genAsmArch(.hsail, inst),
331 .hsail64 => return self.genAsmArch(.hsail64, inst),
332 .spir => return self.genAsmArch(.spir, inst),
333 .spir64 => return self.genAsmArch(.spir64, inst),
334 .kalimba => return self.genAsmArch(.kalimba, inst),
335 .shave => return self.genAsmArch(.shave, inst),
336 .lanai => return self.genAsmArch(.lanai, inst),
337 .wasm32 => return self.genAsmArch(.wasm32, inst),
338 .wasm64 => return self.genAsmArch(.wasm64, inst),
339 .renderscript32 => return self.genAsmArch(.renderscript32, inst),
340 .renderscript64 => return self.genAsmArch(.renderscript64, inst),
341 .ve => return self.genAsmArch(.ve, inst),
342 }
343 }
344
345 fn genAsmArch(self: *Function, comptime arch: Target.Cpu.Arch, inst: *ir.Inst.Assembly) !MCValue {
346 if (arch != .x86_64 and arch != .i386) {883 if (arch != .x86_64 and arch != .i386) {
347 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});884 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
348 }885 }
...@@ -384,30 +921,49 @@ const Function = struct {...@@ -384,30 +921,49 @@ const Function = struct {
384 /// resulting REX is meaningful, but will remain the same if it is not.921 /// resulting REX is meaningful, but will remain the same if it is not.
385 /// * Deliberately inserting a "meaningless REX" requires explicit usage of922 /// * Deliberately inserting a "meaningless REX" requires explicit usage of
386 /// 0x40, and cannot be done via this function.923 /// 0x40, and cannot be done via this function.
387 fn REX(self: *Function, arg: struct { B: bool = false, W: bool = false, X: bool = false, R: bool = false }) !void {924 fn rex(self: *Function, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {
388 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.925 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
389 var value: u8 = 0x40;926 var value: u8 = 0x40;
390 if (arg.B) {927 if (arg.b) {
391 value |= 0x1;928 value |= 0x1;
392 }929 }
393 if (arg.X) {930 if (arg.x) {
394 value |= 0x2;931 value |= 0x2;
395 }932 }
396 if (arg.R) {933 if (arg.r) {
397 value |= 0x4;934 value |= 0x4;
398 }935 }
399 if (arg.W) {936 if (arg.w) {
400 value |= 0x8;937 value |= 0x8;
401 }938 }
402 if (value != 0x40) {939 if (value != 0x40) {
403 try self.code.append(value);940 self.code.appendAssumeCapacity(value);
404 }941 }
405 }942 }
406943
407 fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {944 fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
408 switch (arch) {945 switch (arch) {
409 .x86_64 => switch (mcv) {946 .x86_64 => switch (mcv) {
410 .none, .unreach => unreachable,947 .dead => unreachable,
948 .none => unreachable,
949 .unreach => unreachable,
950 .compare_flags_unsigned => |op| {
951 try self.code.ensureCapacity(self.code.items.len + 3);
952 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
953 const opcode: u8 = switch (op) {
954 .gte => 0x93,
955 .gt => 0x97,
956 .neq => 0x95,
957 .lt => 0x92,
958 .lte => 0x96,
959 .eq => 0x94,
960 };
961 const id = @as(u8, reg.id() & 0b111);
962 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
963 },
964 .compare_flags_signed => |op| {
965 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
966 },
411 .immediate => |x| {967 .immediate => |x| {
412 if (reg.size() != 64) {968 if (reg.size() != 64) {
413 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});969 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
...@@ -426,11 +982,11 @@ const Function = struct {...@@ -426,11 +982,11 @@ const Function = struct {
426 // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since982 // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since
427 // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.983 // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.
428 // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.984 // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.
429 try self.REX(.{ .R = reg.isExtended(), .B = reg.isExtended() });985 try self.code.ensureCapacity(self.code.items.len + 3);
986 self.rex(.{ .r = reg.isExtended(), .b = reg.isExtended() });
430 const id = @as(u8, reg.id() & 0b111);987 const id = @as(u8, reg.id() & 0b111);
431 return self.code.appendSlice(&[_]u8{988 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });
432 0x31, 0xC0 | id << 3 | id,989 return;
433 });
434 }990 }
435 if (x <= std.math.maxInt(u32)) {991 if (x <= std.math.maxInt(u32)) {
436 // Next best case: if we set the lower four bytes, the upper four will be zeroed.992 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
...@@ -463,9 +1019,9 @@ const Function = struct {...@@ -463,9 +1019,9 @@ const Function = struct {
463 // Since we always need a REX here, let's just check if we also need to set REX.B.1019 // Since we always need a REX here, let's just check if we also need to set REX.B.
464 //1020 //
465 // In this case, the encoding of the REX byte is 0b0100100B1021 // In this case, the encoding of the REX byte is 0b0100100B
4661022 try self.code.ensureCapacity(self.code.items.len + 10);
467 try self.REX(.{ .W = true, .B = reg.isExtended() });1023 self.rex(.{ .w = true, .b = reg.isExtended() });
468 try self.code.resize(self.code.items.len + 9);1024 self.code.items.len += 9;
469 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);1025 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
470 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];1026 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
471 mem.writeIntLittle(u64, imm_ptr, x);1027 mem.writeIntLittle(u64, imm_ptr, x);
...@@ -476,13 +1032,13 @@ const Function = struct {...@@ -476,13 +1032,13 @@ const Function = struct {
476 }1032 }
477 // We need the offset from RIP in a signed i32 twos complement.1033 // We need the offset from RIP in a signed i32 twos complement.
478 // The instruction is 7 bytes long and RIP points to the next instruction.1034 // The instruction is 7 bytes long and RIP points to the next instruction.
479 //1035 try self.code.ensureCapacity(self.code.items.len + 7);
480 // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified,1036 // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified,
481 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three1037 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
482 // bits as five.1038 // bits as five.
483 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.1039 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.
484 try self.REX(.{ .W = true, .B = reg.isExtended() });1040 self.rex(.{ .w = true, .b = reg.isExtended() });
485 try self.code.resize(self.code.items.len + 6);1041 self.code.items.len += 6;
486 const rip = self.code.items.len;1042 const rip = self.code.items.len;
487 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);1043 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
488 const offset = @intCast(i32, big_offset);1044 const offset = @intCast(i32, big_offset);
...@@ -502,9 +1058,10 @@ const Function = struct {...@@ -502,9 +1058,10 @@ const Function = struct {
502 // If the *source* is extended, the B field must be 1.1058 // If the *source* is extended, the B field must be 1.
503 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle1059 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
504 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.1060 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
505 try self.REX(.{ .W = true, .R = reg.isExtended(), .B = src_reg.isExtended() });1061 try self.code.ensureCapacity(self.code.items.len + 3);
1062 self.rex(.{ .w = true, .r = reg.isExtended(), .b = src_reg.isExtended() });
506 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);1063 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);
507 try self.code.appendSlice(&[_]u8{ 0x8B, R });1064 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
508 },1065 },
509 .memory => |x| {1066 .memory => |x| {
510 if (reg.size() != 64) {1067 if (reg.size() != 64) {
...@@ -518,14 +1075,14 @@ const Function = struct {...@@ -518,14 +1075,14 @@ const Function = struct {
518 // The SIB must be 0x25, to indicate a disp32 with no scaled index.1075 // The SIB must be 0x25, to indicate a disp32 with no scaled index.
519 // 0b00RRR100, where RRR is the lower three bits of the register ID.1076 // 0b00RRR100, where RRR is the lower three bits of the register ID.
520 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.1077 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
521 try self.REX(.{ .W = true, .B = reg.isExtended() });1078 try self.code.ensureCapacity(self.code.items.len + 8);
522 try self.code.resize(self.code.items.len + 7);1079 self.rex(.{ .w = true, .b = reg.isExtended() });
523 const r = 0x04 | (@as(u8, reg.id() & 0b111) << 3);1080 self.code.appendSliceAssumeCapacity(&[_]u8{
524 self.code.items[self.code.items.len - 7] = 0x8B;1081 0x8B,
525 self.code.items[self.code.items.len - 6] = r;1082 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
526 self.code.items[self.code.items.len - 5] = 0x25;1083 0x25,
527 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];1084 });
528 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));1085 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, x));
529 } else {1086 } else {
530 // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load1087 // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load
531 // the value.1088 // the value.
...@@ -556,18 +1113,21 @@ const Function = struct {...@@ -556,18 +1113,21 @@ const Function = struct {
556 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.1113 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
557 // TODO: determine whether to allow other sized registers, and if so, handle them properly.1114 // TODO: determine whether to allow other sized registers, and if so, handle them properly.
558 // This operation requires three bytes: REX 0x8B R/M1115 // This operation requires three bytes: REX 0x8B R/M
559 //1116 try self.code.ensureCapacity(self.code.items.len + 3);
560 // For this operation, we want R/M mode *zero* (use register indirectly), and the two register1117 // For this operation, we want R/M mode *zero* (use register indirectly), and the two register
561 // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.1118 // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.
562 //1119 //
563 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*1120 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
564 // register operands need to be marked as extended.1121 // register operands need to be marked as extended.
565 try self.REX(.{ .W = true, .B = reg.isExtended(), .R = reg.isExtended() });1122 self.rex(.{ .w = true, .b = reg.isExtended(), .r = reg.isExtended() });
566 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());1123 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
567 try self.code.appendSlice(&[_]u8{ 0x8B, RM });1124 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
568 }1125 }
569 }1126 }
570 },1127 },
1128 .stack_offset => |off| {
1129 return self.fail(src, "TODO implement genSetReg for stack variables", .{});
1130 },
571 },1131 },
572 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),1132 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
573 }1133 }
...@@ -584,22 +1144,59 @@ const Function = struct {...@@ -584,22 +1144,59 @@ const Function = struct {
584 }1144 }
5851145
586 fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue {1146 fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue {
587 if (self.inst_table.getValue(inst)) |mcv| {1147 // Constants have static lifetimes, so they are always memoized in the outer most table.
588 return mcv;
589 }
590 if (inst.cast(ir.Inst.Constant)) |const_inst| {1148 if (inst.cast(ir.Inst.Constant)) |const_inst| {
591 const mcvalue = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });1149 const branch = &self.branch_stack.items[0];
592 try self.inst_table.putNoClobber(inst, mcvalue);1150 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
593 return mcvalue;1151 if (!gop.found_existing) {
594 } else {1152 gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
595 return self.inst_table.getValue(inst).?;1153 }
1154 return gop.entry.value;
1155 }
1156
1157 // Treat each stack item as a "layer" on top of the previous one.
1158 var i: usize = self.branch_stack.items.len;
1159 while (true) {
1160 i -= 1;
1161 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1162 return mcv;
1163 }
596 }1164 }
597 }1165 }
5981166
1167 fn copyToNewRegister(self: *Function, inst: *ir.Inst) !MCValue {
1168 return self.fail(inst.src, "TODO implement copyToNewRegister", .{});
1169 }
1170
1171 /// If the MCValue is an immediate, and it does not fit within this type,
1172 /// we put it in a register.
1173 /// A potential opportunity for future optimization here would be keeping track
1174 /// of the fact that the instruction is available both as an immediate
1175 /// and as a register.
1176 fn limitImmediateType(self: *Function, inst: *ir.Inst, comptime T: type) !MCValue {
1177 const mcv = try self.resolveInst(inst);
1178 const ti = @typeInfo(T).Int;
1179 switch (mcv) {
1180 .immediate => |imm| {
1181 // This immediate is unsigned.
1182 const U = @Type(.{
1183 .Int = .{
1184 .bits = ti.bits - @boolToInt(ti.is_signed),
1185 .is_signed = false,
1186 },
1187 });
1188 if (imm >= std.math.maxInt(U)) {
1189 return self.copyToNewRegister(inst);
1190 }
1191 },
1192 else => {},
1193 }
1194 return mcv;
1195 }
1196
599 fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue {1197 fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue {
600 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1198 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
601 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1199 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
602 const allocator = self.code.allocator;
603 switch (typed_value.ty.zigTypeTag()) {1200 switch (typed_value.ty.zigTypeTag()) {
604 .Pointer => {1201 .Pointer => {
605 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {1202 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
...@@ -617,16 +1214,61 @@ const Function = struct {...@@ -617,16 +1214,61 @@ const Function = struct {
617 }1214 }
618 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };1215 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
619 },1216 },
1217 .Bool => {
1218 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1219 },
620 .ComptimeInt => unreachable, // semantic analysis prevents this1220 .ComptimeInt => unreachable, // semantic analysis prevents this
621 .ComptimeFloat => unreachable, // semantic analysis prevents this1221 .ComptimeFloat => unreachable, // semantic analysis prevents this
622 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),1222 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
623 }1223 }
624 }1224 }
6251225
626 fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } {1226 fn resolveParameters(
1227 self: *Function,
1228 src: usize,
1229 cc: std.builtin.CallingConvention,
1230 param_types: []const Type,
1231 results: []MCValue,
1232 ) !u32 {
1233 switch (self.target.cpu.arch) {
1234 .x86_64 => {
1235 switch (cc) {
1236 .Naked => {
1237 assert(results.len == 0);
1238 return 0;
1239 },
1240 .Unspecified, .C => {
1241 var next_int_reg: usize = 0;
1242 var next_stack_offset: u32 = 0;
1243
1244 const integer_registers = [_]Reg(.x86_64){ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
1245 for (param_types) |ty, i| {
1246 switch (ty.zigTypeTag()) {
1247 .Bool, .Int => {
1248 if (next_int_reg >= integer_registers.len) {
1249 results[i] = .{ .stack_offset = next_stack_offset };
1250 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
1251 } else {
1252 results[i] = .{ .register = @enumToInt(integer_registers[next_int_reg]) };
1253 next_int_reg += 1;
1254 }
1255 },
1256 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
1257 }
1258 }
1259 return next_stack_offset;
1260 },
1261 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1262 }
1263 },
1264 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),
1265 }
1266 }
1267
1268 fn fail(self: *Function, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
627 @setCold(true);1269 @setCold(true);
628 assert(self.err_msg == null);1270 assert(self.err_msg == null);
629 self.err_msg = try ErrorMsg.create(self.code.allocator, src, format, args);1271 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
630 return error.CodegenFail;1272 return error.CodegenFail;
631 }1273 }
632};1274};
src-self-hosted/codegen/c.zig created+206
...@@ -0,0 +1,206 @@
1const std = @import("std");
2
3const link = @import("../link.zig");
4const Module = @import("../Module.zig");
5
6const Inst = @import("../ir.zig").Inst;
7const Value = @import("../value.zig").Value;
8const Type = @import("../type.zig").Type;
9
10const C = link.File.C;
11const Decl = Module.Decl;
12const mem = std.mem;
13
14/// Maps a name from Zig source to C. This will always give the same output for
15/// any given input.
16fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
17 return allocator.dupe(u8, name);
18}
19
20fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type, src: usize) !void {
21 if (T.tag() == .usize) {
22 file.need_stddef = true;
23 try writer.writeAll("size_t");
24 } else {
25 switch (T.zigTypeTag()) {
26 .NoReturn => {
27 file.need_noreturn = true;
28 try writer.writeAll("noreturn void");
29 },
30 .Void => try writer.writeAll("void"),
31 .Int => {
32 if (T.tag() == .u8) {
33 file.need_stdint = true;
34 try writer.writeAll("uint8_t");
35 } else {
36 return file.fail(src, "TODO implement int types", .{});
37 }
38 },
39 else => |e| return file.fail(src, "TODO implement type {}", .{e}),
40 }
41 }
42}
43
44fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
45 const tv = decl.typed_value.most_recent.typed_value;
46 try renderType(file, writer, tv.ty.fnReturnType(), decl.src());
47 const name = try map(file.allocator, mem.spanZ(decl.name));
48 defer file.allocator.free(name);
49 try writer.print(" {}(", .{name});
50 if (tv.ty.fnParamLen() == 0)
51 try writer.writeAll("void)")
52 else
53 return file.fail(decl.src(), "TODO implement parameters", .{});
54}
55
56pub fn generate(file: *C, decl: *Decl) !void {
57 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
58 .Fn => try genFn(file, decl),
59 .Array => try genArray(file, decl),
60 else => |e| return file.fail(decl.src(), "TODO {}", .{e}),
61 }
62}
63
64fn genArray(file: *C, decl: *Decl) !void {
65 const tv = decl.typed_value.most_recent.typed_value;
66 // TODO: prevent inline asm constants from being emitted
67 const name = try map(file.allocator, mem.span(decl.name));
68 defer file.allocator.free(name);
69 if (tv.val.cast(Value.Payload.Bytes)) |payload|
70 if (tv.ty.arraySentinel()) |sentinel|
71 if (sentinel.toUnsignedInt() == 0)
72 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
73 else
74 return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{})
75 else
76 return file.fail(decl.src(), "TODO byte arrays without sentinels", .{})
77 else
78 return file.fail(decl.src(), "TODO non-byte arrays", .{});
79}
80
81fn genFn(file: *C, decl: *Decl) !void {
82 const writer = file.main.writer();
83 const tv = decl.typed_value.most_recent.typed_value;
84
85 try renderFunctionSignature(file, writer, decl);
86
87 try writer.writeAll(" {");
88
89 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
90 const instructions = func.analysis.success.instructions;
91 if (instructions.len > 0) {
92 for (instructions) |inst| {
93 try writer.writeAll("\n\t");
94 switch (inst.tag) {
95 .assembly => try genAsm(file, inst.cast(Inst.Assembly).?, decl),
96 .call => try genCall(file, inst.cast(Inst.Call).?, decl),
97 .ret => try genRet(file, inst.cast(Inst.Ret).?, decl, tv.ty.fnReturnType()),
98 .retvoid => try file.main.writer().print("return;", .{}),
99 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
100 }
101 }
102 try writer.writeAll("\n");
103 }
104
105 try writer.writeAll("}\n\n");
106}
107
108fn genRet(file: *C, inst: *Inst.Ret, decl: *Decl, expected_return_type: Type) !void {
109 const writer = file.main.writer();
110 const ret_value = inst.args.operand;
111 const value = ret_value.value().?;
112 if (expected_return_type.eql(ret_value.ty))
113 return file.fail(decl.src(), "TODO return {}", .{expected_return_type})
114 else if (expected_return_type.isInt() and ret_value.ty.tag() == .comptime_int)
115 if (value.intFitsInType(expected_return_type, file.options.target))
116 if (expected_return_type.intInfo(file.options.target).bits <= 64)
117 try writer.print("return {};", .{value.toUnsignedInt()})
118 else
119 return file.fail(decl.src(), "TODO return ints > 64 bits", .{})
120 else
121 return file.fail(decl.src(), "comptime int {} does not fit in {}", .{ value.toUnsignedInt(), expected_return_type })
122 else
123 return file.fail(decl.src(), "return type mismatch: expected {}, found {}", .{ expected_return_type, ret_value.ty });
124}
125
126fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
127 const writer = file.main.writer();
128 const header = file.header.writer();
129 if (inst.args.func.cast(Inst.Constant)) |func_inst| {
130 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
131 const target = func_val.func.owner_decl;
132 const target_ty = target.typed_value.most_recent.typed_value.ty;
133 const ret_ty = target_ty.fnReturnType().tag();
134 if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
135 try writer.print("(void)", .{});
136 }
137 const tname = mem.spanZ(target.name);
138 if (file.called.get(tname) == null) {
139 try file.called.put(tname, void{});
140 try renderFunctionSignature(file, header, target);
141 try header.writeAll(";\n");
142 }
143 try writer.print("{}();", .{tname});
144 } else {
145 return file.fail(decl.src(), "TODO non-function call target?", .{});
146 }
147 if (inst.args.args.len != 0) {
148 return file.fail(decl.src(), "TODO function arguments", .{});
149 }
150 } else {
151 return file.fail(decl.src(), "TODO non-constant call inst?", .{});
152 }
153}
154
155fn genAsm(file: *C, inst: *Inst.Assembly, decl: *Decl) !void {
156 const as = inst.args;
157 const writer = file.main.writer();
158 for (as.inputs) |i, index| {
159 if (i[0] == '{' and i[i.len - 1] == '}') {
160 const reg = i[1 .. i.len - 1];
161 const arg = as.args[index];
162 if (arg.cast(Inst.Constant)) |c| {
163 if (c.val.tag() == .int_u64) {
164 try writer.writeAll("register ");
165 try renderType(file, writer, arg.ty, decl.src());
166 try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() });
167 } else {
168 return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()});
169 }
170 } else {
171 return file.fail(decl.src(), "TODO non-constant inline asm args", .{});
172 }
173 } else {
174 return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{});
175 }
176 }
177 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
178 if (as.output) |o| {
179 return file.fail(decl.src(), "TODO inline asm output", .{});
180 }
181 if (as.inputs.len > 0) {
182 if (as.output == null) {
183 try writer.writeAll(" :");
184 }
185 try writer.writeAll(": ");
186 for (as.inputs) |i, index| {
187 if (i[0] == '{' and i[i.len - 1] == '}') {
188 const reg = i[1 .. i.len - 1];
189 const arg = as.args[index];
190 if (index > 0) {
191 try writer.writeAll(", ");
192 }
193 if (arg.cast(Inst.Constant)) |c| {
194 try writer.print("\"\"({}_constant)", .{reg});
195 } else {
196 // This is blocked by the earlier test
197 unreachable;
198 }
199 } else {
200 // This is blocked by the earlier test
201 unreachable;
202 }
203 }
204 }
205 try writer.writeAll(");");
206}
src-self-hosted/codegen/x86_64.zig+6-5
...@@ -1,20 +1,21 @@...@@ -1,20 +1,21 @@
1const Type = @import("../Type.zig");
2
1// zig fmt: off3// zig fmt: off
24
3/// Definitions of all of the x64 registers. The order is very, very important.5/// Definitions of all of the x64 registers. The order is semantically meaningful.
4/// The registers are defined such that IDs go in descending order of 64-bit,6/// The registers are defined such that IDs go in descending order of 64-bit,
5/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen7/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
6/// registers. This results in some very, very useful properties:8/// registers. This results in some useful properties:
7///9///
8/// Any 64-bit register can be turned into its 32-bit form by adding 16, and10/// Any 64-bit register can be turned into its 32-bit form by adding 16, and
9/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it11/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it
10/// works for all except for sp, bp, si, and di, which don't *have* an 8-bit12/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit
11/// form.13/// form.
12///14///
13/// If (register & 8) is set, the register is extended.15/// If (register & 8) is set, the register is extended.
14///16///
15/// The ID can be easily determined by figuring out what range the register is17/// The ID can be easily determined by figuring out what range the register is
16/// in, and then subtracting the base.18/// in, and then subtracting the base.
17///
18pub const Register = enum(u8) {19pub const Register = enum(u8) {
19 // 0 through 15, 64-bit registers. 8-15 are extended.20 // 0 through 15, 64-bit registers. 8-15 are extended.
20 // id is just the int value.21 // id is just the int value.
...@@ -66,4 +67,4 @@ pub const Register = enum(u8) {...@@ -66,4 +67,4 @@ pub const Register = enum(u8) {
66 }67 }
67};68};
6869
69// zig fmt: on70// zig fmt: on
\ No newline at end of file
src-self-hosted/dep_tokenizer.zig+13-13
...@@ -299,12 +299,12 @@ pub const Tokenizer = struct {...@@ -299,12 +299,12 @@ pub const Tokenizer = struct {
299 return null;299 return null;
300 }300 }
301301
302 fn errorf(self: *Tokenizer, comptime fmt: []const u8, args: var) Error {302 fn errorf(self: *Tokenizer, comptime fmt: []const u8, args: anytype) Error {
303 self.error_text = try std.fmt.allocPrintZ(&self.arena.allocator, fmt, args);303 self.error_text = try std.fmt.allocPrintZ(&self.arena.allocator, fmt, args);
304 return Error.InvalidInput;304 return Error.InvalidInput;
305 }305 }
306306
307 fn errorPosition(self: *Tokenizer, position: usize, bytes: []const u8, comptime fmt: []const u8, args: var) Error {307 fn errorPosition(self: *Tokenizer, position: usize, bytes: []const u8, comptime fmt: []const u8, args: anytype) Error {
308 var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0);308 var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0);
309 try buffer.outStream().print(fmt, args);309 try buffer.outStream().print(fmt, args);
310 try buffer.appendSlice(" '");310 try buffer.appendSlice(" '");
...@@ -316,7 +316,7 @@ pub const Tokenizer = struct {...@@ -316,7 +316,7 @@ pub const Tokenizer = struct {
316 return Error.InvalidInput;316 return Error.InvalidInput;
317 }317 }
318318
319 fn errorIllegalChar(self: *Tokenizer, position: usize, char: u8, comptime fmt: []const u8, args: var) Error {319 fn errorIllegalChar(self: *Tokenizer, position: usize, char: u8, comptime fmt: []const u8, args: anytype) Error {
320 var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0);320 var buffer = try std.ArrayListSentineled(u8, 0).initSize(&self.arena.allocator, 0);
321 try buffer.appendSlice("illegal char ");321 try buffer.appendSlice("illegal char ");
322 try printUnderstandableChar(&buffer, char);322 try printUnderstandableChar(&buffer, char);
...@@ -883,7 +883,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {...@@ -883,7 +883,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void {
883 testing.expect(false);883 testing.expect(false);
884}884}
885885
886fn printSection(out: var, label: []const u8, bytes: []const u8) !void {886fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void {
887 try printLabel(out, label, bytes);887 try printLabel(out, label, bytes);
888 try hexDump(out, bytes);888 try hexDump(out, bytes);
889 try printRuler(out);889 try printRuler(out);
...@@ -891,7 +891,7 @@ fn printSection(out: var, label: []const u8, bytes: []const u8) !void {...@@ -891,7 +891,7 @@ fn printSection(out: var, label: []const u8, bytes: []const u8) !void {
891 try out.write("\n");891 try out.write("\n");
892}892}
893893
894fn printLabel(out: var, label: []const u8, bytes: []const u8) !void {894fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void {
895 var buf: [80]u8 = undefined;895 var buf: [80]u8 = undefined;
896 var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len });896 var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len });
897 try out.write(text);897 try out.write(text);
...@@ -903,7 +903,7 @@ fn printLabel(out: var, label: []const u8, bytes: []const u8) !void {...@@ -903,7 +903,7 @@ fn printLabel(out: var, label: []const u8, bytes: []const u8) !void {
903 try out.write("\n");903 try out.write("\n");
904}904}
905905
906fn printRuler(out: var) !void {906fn printRuler(out: anytype) !void {
907 var i: usize = 0;907 var i: usize = 0;
908 const end = 79;908 const end = 79;
909 while (i < 79) : (i += 1) {909 while (i < 79) : (i += 1) {
...@@ -912,7 +912,7 @@ fn printRuler(out: var) !void {...@@ -912,7 +912,7 @@ fn printRuler(out: var) !void {
912 try out.write("\n");912 try out.write("\n");
913}913}
914914
915fn hexDump(out: var, bytes: []const u8) !void {915fn hexDump(out: anytype, bytes: []const u8) !void {
916 const n16 = bytes.len >> 4;916 const n16 = bytes.len >> 4;
917 var line: usize = 0;917 var line: usize = 0;
918 var offset: usize = 0;918 var offset: usize = 0;
...@@ -959,7 +959,7 @@ fn hexDump(out: var, bytes: []const u8) !void {...@@ -959,7 +959,7 @@ fn hexDump(out: var, bytes: []const u8) !void {
959 try out.write("\n");959 try out.write("\n");
960}960}
961961
962fn hexDump16(out: var, offset: usize, bytes: []const u8) !void {962fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void {
963 try printDecValue(out, offset, 8);963 try printDecValue(out, offset, 8);
964 try out.write(":");964 try out.write(":");
965 try out.write(" ");965 try out.write(" ");
...@@ -977,19 +977,19 @@ fn hexDump16(out: var, offset: usize, bytes: []const u8) !void {...@@ -977,19 +977,19 @@ fn hexDump16(out: var, offset: usize, bytes: []const u8) !void {
977 try out.write("|\n");977 try out.write("|\n");
978}978}
979979
980fn printDecValue(out: var, value: u64, width: u8) !void {980fn printDecValue(out: anytype, value: u64, width: u8) !void {
981 var buffer: [20]u8 = undefined;981 var buffer: [20]u8 = undefined;
982 const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, width);982 const len = std.fmt.formatIntBuf(buffer[0..], value, 10, false, width);
983 try out.write(buffer[0..len]);983 try out.write(buffer[0..len]);
984}984}
985985
986fn printHexValue(out: var, value: u64, width: u8) !void {986fn printHexValue(out: anytype, value: u64, width: u8) !void {
987 var buffer: [16]u8 = undefined;987 var buffer: [16]u8 = undefined;
988 const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, width);988 const len = std.fmt.formatIntBuf(buffer[0..], value, 16, false, width);
989 try out.write(buffer[0..len]);989 try out.write(buffer[0..len]);
990}990}
991991
992fn printCharValues(out: var, bytes: []const u8) !void {992fn printCharValues(out: anytype, bytes: []const u8) !void {
993 for (bytes) |b| {993 for (bytes) |b| {
994 try out.write(&[_]u8{printable_char_tab[b]});994 try out.write(&[_]u8{printable_char_tab[b]});
995 }995 }
...@@ -1020,13 +1020,13 @@ comptime {...@@ -1020,13 +1020,13 @@ comptime {
1020// output: must be a function that takes a `self` idiom parameter1020// output: must be a function that takes a `self` idiom parameter
1021// and a bytes parameter1021// and a bytes parameter
1022// context: must be that self1022// context: must be that self
1023fn makeOutput(comptime output: var, context: var) Output(output, @TypeOf(context)) {1023fn makeOutput(comptime output: anytype, context: anytype) Output(output, @TypeOf(context)) {
1024 return Output(output, @TypeOf(context)){1024 return Output(output, @TypeOf(context)){
1025 .context = context,1025 .context = context,
1026 };1026 };
1027}1027}
10281028
1029fn Output(comptime output_func: var, comptime Context: type) type {1029fn Output(comptime output_func: anytype, comptime Context: type) type {
1030 return struct {1030 return struct {
1031 context: Context,1031 context: Context,
10321032
src-self-hosted/ir.zig+121-2
...@@ -2,6 +2,8 @@ const std = @import("std");...@@ -2,6 +2,8 @@ const std = @import("std");
2const Value = @import("value.zig").Value;2const Value = @import("value.zig").Value;
3const Type = @import("type.zig").Type;3const Type = @import("type.zig").Type;
4const Module = @import("Module.zig");4const Module = @import("Module.zig");
5const assert = std.debug.assert;
6const codegen = @import("codegen.zig");
57
6/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation8/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
7/// of instructions that correspond to the ZIR text format.9/// of instructions that correspond to the ZIR text format.
...@@ -10,14 +12,48 @@ const Module = @import("Module.zig");...@@ -10,14 +12,48 @@ const Module = @import("Module.zig");
10/// a memory location for the value to survive after a const instruction.12/// a memory location for the value to survive after a const instruction.
11pub const Inst = struct {13pub const Inst = struct {
12 tag: Tag,14 tag: Tag,
15 /// Each bit represents the index of an `Inst` parameter in the `args` field.
16 /// If a bit is set, it marks the end of the lifetime of the corresponding
17 /// instruction parameter. For example, 0b101 means that the first and
18 /// third `Inst` parameters' lifetimes end after this instruction, and will
19 /// not have any more following references.
20 /// The most significant bit being set means that the instruction itself is
21 /// never referenced, in other words its lifetime ends as soon as it finishes.
22 /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced.
23 /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
24 /// lifetimes of operands are encoded elsewhere.
25 deaths: DeathsInt = undefined,
13 ty: Type,26 ty: Type,
14 /// Byte offset into the source.27 /// Byte offset into the source.
15 src: usize,28 src: usize,
1629
30 pub const DeathsInt = u16;
31 pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
32 pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1;
33 pub const deaths_bits = unreferenced_bit_index - 1;
34
35 pub fn isUnused(self: Inst) bool {
36 return (self.deaths & (1 << unreferenced_bit_index)) != 0;
37 }
38
39 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
40 assert(index < deaths_bits);
41 return @truncate(u1, self.deaths << index) != 0;
42 }
43
44 pub fn specialOperandDeaths(self: Inst) bool {
45 return (self.deaths & (1 << deaths_bits)) != 0;
46 }
47
17 pub const Tag = enum {48 pub const Tag = enum {
49 add,
50 arg,
18 assembly,51 assembly,
19 bitcast,52 bitcast,
53 block,
54 br,
20 breakpoint,55 breakpoint,
56 brvoid,
21 call,57 call,
22 cmp,58 cmp,
23 condbr,59 condbr,
...@@ -26,7 +62,10 @@ pub const Inst = struct {...@@ -26,7 +62,10 @@ pub const Inst = struct {
26 isnull,62 isnull,
27 ptrtoint,63 ptrtoint,
28 ret,64 ret,
65 retvoid,
66 sub,
29 unreach,67 unreach,
68 not,
30 };69 };
3170
32 pub fn cast(base: *Inst, comptime T: type) ?*T {71 pub fn cast(base: *Inst, comptime T: type) ?*T {
...@@ -49,6 +88,22 @@ pub const Inst = struct {...@@ -49,6 +88,22 @@ pub const Inst = struct {
49 return inst.val;88 return inst.val;
50 }89 }
5190
91 pub const Add = struct {
92 pub const base_tag = Tag.add;
93 base: Inst,
94
95 args: struct {
96 lhs: *Inst,
97 rhs: *Inst,
98 },
99 };
100
101 pub const Arg = struct {
102 pub const base_tag = Tag.arg;
103 base: Inst,
104 args: void,
105 };
106
52 pub const Assembly = struct {107 pub const Assembly = struct {
53 pub const base_tag = Tag.assembly;108 pub const base_tag = Tag.assembly;
54 base: Inst,109 base: Inst,
...@@ -72,12 +127,39 @@ pub const Inst = struct {...@@ -72,12 +127,39 @@ pub const Inst = struct {
72 },127 },
73 };128 };
74129
130 pub const Block = struct {
131 pub const base_tag = Tag.block;
132 base: Inst,
133 args: struct {
134 body: Body,
135 },
136 /// This memory is reserved for codegen code to do whatever it needs to here.
137 codegen: codegen.BlockData = .{},
138 };
139
140 pub const Br = struct {
141 pub const base_tag = Tag.br;
142 base: Inst,
143 args: struct {
144 block: *Block,
145 operand: *Inst,
146 },
147 };
148
75 pub const Breakpoint = struct {149 pub const Breakpoint = struct {
76 pub const base_tag = Tag.breakpoint;150 pub const base_tag = Tag.breakpoint;
77 base: Inst,151 base: Inst,
78 args: void,152 args: void,
79 };153 };
80154
155 pub const BrVoid = struct {
156 pub const base_tag = Tag.brvoid;
157 base: Inst,
158 args: struct {
159 block: *Block,
160 },
161 };
162
81 pub const Call = struct {163 pub const Call = struct {
82 pub const base_tag = Tag.call;164 pub const base_tag = Tag.call;
83 base: Inst,165 base: Inst,
...@@ -104,8 +186,23 @@ pub const Inst = struct {...@@ -104,8 +186,23 @@ pub const Inst = struct {
104 base: Inst,186 base: Inst,
105 args: struct {187 args: struct {
106 condition: *Inst,188 condition: *Inst,
107 true_body: Module.Body,189 true_body: Body,
108 false_body: Module.Body,190 false_body: Body,
191 },
192 /// Set of instructions whose lifetimes end at the start of one of the branches.
193 /// The `true` branch is first: `deaths[0..true_death_count]`.
194 /// The `false` branch is next: `(deaths + true_death_count)[..false_death_count]`.
195 deaths: [*]*Inst = undefined,
196 true_death_count: u32 = 0,
197 false_death_count: u32 = 0,
198 };
199
200 pub const Not = struct {
201 pub const base_tag = Tag.not;
202
203 base: Inst,
204 args: struct {
205 operand: *Inst,
109 },206 },
110 };207 };
111208
...@@ -146,12 +243,34 @@ pub const Inst = struct {...@@ -146,12 +243,34 @@ pub const Inst = struct {
146 pub const Ret = struct {243 pub const Ret = struct {
147 pub const base_tag = Tag.ret;244 pub const base_tag = Tag.ret;
148 base: Inst,245 base: Inst,
246 args: struct {
247 operand: *Inst,
248 },
249 };
250
251 pub const RetVoid = struct {
252 pub const base_tag = Tag.retvoid;
253 base: Inst,
149 args: void,254 args: void,
150 };255 };
151256
257 pub const Sub = struct {
258 pub const base_tag = Tag.sub;
259 base: Inst,
260
261 args: struct {
262 lhs: *Inst,
263 rhs: *Inst,
264 },
265 };
266
152 pub const Unreach = struct {267 pub const Unreach = struct {
153 pub const base_tag = Tag.unreach;268 pub const base_tag = Tag.unreach;
154 base: Inst,269 base: Inst,
155 args: void,270 args: void,
156 };271 };
157};272};
273
274pub const Body = struct {
275 instructions: []*Inst,
276};
src-self-hosted/libc_installation.zig+2-2
...@@ -37,7 +37,7 @@ pub const LibCInstallation = struct {...@@ -37,7 +37,7 @@ pub const LibCInstallation = struct {
37 pub fn parse(37 pub fn parse(
38 allocator: *Allocator,38 allocator: *Allocator,
39 libc_file: []const u8,39 libc_file: []const u8,
40 stderr: var,40 stderr: anytype,
41 ) !LibCInstallation {41 ) !LibCInstallation {
42 var self: LibCInstallation = .{};42 var self: LibCInstallation = .{};
4343
...@@ -115,7 +115,7 @@ pub const LibCInstallation = struct {...@@ -115,7 +115,7 @@ pub const LibCInstallation = struct {
115 return self;115 return self;
116 }116 }
117117
118 pub fn render(self: LibCInstallation, out: var) !void {118 pub fn render(self: LibCInstallation, out: anytype) !void {
119 @setEvalBranchQuota(4000);119 @setEvalBranchQuota(4000);
120 const include_dir = self.include_dir orelse "";120 const include_dir = self.include_dir orelse "";
121 const sys_include_dir = self.sys_include_dir orelse "";121 const sys_include_dir = self.sys_include_dir orelse "";
src-self-hosted/link.zig+1321-1108
...@@ -7,6 +7,7 @@ const Module = @import("Module.zig");...@@ -7,6 +7,7 @@ const Module = @import("Module.zig");
7const fs = std.fs;7const fs = std.fs;
8const elf = std.elf;8const elf = std.elf;
9const codegen = @import("codegen.zig");9const codegen = @import("codegen.zig");
10const c_codegen = @import("codegen/c.zig");
1011
11const default_entry_addr = 0x8000000;12const default_entry_addr = 0x8000000;
1213
...@@ -32,13 +33,23 @@ pub fn openBinFilePath(...@@ -32,13 +33,23 @@ pub fn openBinFilePath(
32 dir: fs.Dir,33 dir: fs.Dir,
33 sub_path: []const u8,34 sub_path: []const u8,
34 options: Options,35 options: Options,
35) !ElfFile {36) !*File {
36 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) });37 const cbe = options.object_format == .c;
38 const file = try dir.createFile(sub_path, .{ .truncate = cbe, .read = true, .mode = determineMode(options) });
37 errdefer file.close();39 errdefer file.close();
3840
39 var bin_file = try openBinFile(allocator, file, options);41 if (cbe) {
40 bin_file.owns_file_handle = true;42 var bin_file = try allocator.create(File.C);
41 return bin_file;43 errdefer allocator.destroy(bin_file);
44 bin_file.* = try openCFile(allocator, file, options);
45 return &bin_file.base;
46 } else {
47 var bin_file = try allocator.create(File.Elf);
48 errdefer allocator.destroy(bin_file);
49 bin_file.* = try openBinFile(allocator, file, options);
50 bin_file.owns_file_handle = true;
51 return &bin_file.base;
52 }
42}53}
4354
44/// Atomically overwrites the old file, if present.55/// Atomically overwrites the old file, if present.
...@@ -75,12 +86,24 @@ pub fn writeFilePath(...@@ -75,12 +86,24 @@ pub fn writeFilePath(
75 return result;86 return result;
76}87}
7788
89fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C {
90 return File.C{
91 .allocator = allocator,
92 .file = file,
93 .options = options,
94 .main = std.ArrayList(u8).init(allocator),
95 .header = std.ArrayList(u8).init(allocator),
96 .constants = std.ArrayList(u8).init(allocator),
97 .called = std.StringHashMap(void).init(allocator),
98 };
99}
100
78/// Attempts incremental linking, if the file already exists.101/// Attempts incremental linking, if the file already exists.
79/// If incremental linking fails, falls back to truncating the file and rewriting it.102/// If incremental linking fails, falls back to truncating the file and rewriting it.
80/// Returns an error if `file` is not already open with +read +write +seek abilities.103/// Returns an error if `file` is not already open with +read +write +seek abilities.
81/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.104/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
82/// This operation is not atomic.105/// This operation is not atomic.
83pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {106pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
84 return openBinFileInner(allocator, file, options) catch |err| switch (err) {107 return openBinFileInner(allocator, file, options) catch |err| switch (err) {
85 error.IncrFailed => {108 error.IncrFailed => {
86 return createElfFile(allocator, file, options);109 return createElfFile(allocator, file, options);
...@@ -89,514 +112,592 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF...@@ -89,514 +112,592 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF
89 };112 };
90}113}
91114
92pub const ElfFile = struct {115pub const File = struct {
93 allocator: *Allocator,116 tag: Tag,
94 file: ?fs.File,117 pub fn cast(base: *File, comptime T: type) ?*T {
95 owns_file_handle: bool,118 if (base.tag != T.base_tag)
96 options: Options,119 return null;
97 ptr_width: enum { p32, p64 },
98
99 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
100 /// Same order as in the file.
101 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
102 shdr_table_offset: ?u64 = null,
103
104 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
105 /// Same order as in the file.
106 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
107 phdr_table_offset: ?u64 = null,
108 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
109 phdr_load_re_index: ?u16 = null,
110 /// The index into the program headers of the global offset table.
111 /// It needs PT_LOAD and Read flags.
112 phdr_got_index: ?u16 = null,
113 entry_addr: ?u64 = null,
114
115 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
116 shstrtab_index: ?u16 = null,
117
118 text_section_index: ?u16 = null,
119 symtab_section_index: ?u16 = null,
120 got_section_index: ?u16 = null,
121
122 /// The same order as in the file. ELF requires global symbols to all be after the
123 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
124 /// write them at the end. These are only the local symbols. The length of this array
125 /// is the value used for sh_info in the .symtab section.
126 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
127 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
128
129 local_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
130 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
131 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
132
133 /// Same order as in the file. The value is the absolute vaddr value.
134 /// If the vaddr of the executable program header changes, the entire
135 /// offset table needs to be rewritten.
136 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
137
138 phdr_table_dirty: bool = false,
139 shdr_table_dirty: bool = false,
140 shstrtab_dirty: bool = false,
141 offset_table_count_dirty: bool = false,
142
143 error_flags: ErrorFlags = ErrorFlags{},
144
145 /// A list of text blocks that have surplus capacity. This list can have false
146 /// positives, as functions grow and shrink over time, only sometimes being added
147 /// or removed from the freelist.
148 ///
149 /// A text block has surplus capacity when its overcapacity value is greater than
150 /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
151 /// much extra capacity, that we could fit a small new symbol in it, itself with
152 /// ideal_capacity or more.
153 ///
154 /// Ideal capacity is defined by size * alloc_num / alloc_den.
155 ///
156 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
157 /// overcapacity can be negative. A simple way to have negative overcapacity is to
158 /// allocate a fresh text block, which will have ideal capacity, and then grow it
159 /// by 1 byte. It will then have -1 overcapacity.
160 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
161 last_text_block: ?*TextBlock = null,
162
163 /// `alloc_num / alloc_den` is the factor of padding when allocating.
164 const alloc_num = 4;
165 const alloc_den = 3;
166
167 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
168 /// it as a possible place to put new symbols, it must have enough room for this many bytes
169 /// (plus extra for reserved capacity).
170 const minimum_text_block_size = 64;
171 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
172
173 pub const ErrorFlags = struct {
174 no_entry_point_found: bool = false,
175 };
176
177 pub const TextBlock = struct {
178 /// Each decl always gets a local symbol with the fully qualified name.
179 /// The vaddr and size are found here directly.
180 /// The file offset is found by computing the vaddr offset from the section vaddr
181 /// the symbol references, and adding that to the file offset of the section.
182 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
183 /// offset table entry.
184 local_sym_index: u32,
185 /// This field is undefined for symbols with size = 0.
186 offset_table_index: u32,
187 /// Points to the previous and next neighbors, based on the `text_offset`.
188 /// This can be used to find, for example, the capacity of this `TextBlock`.
189 prev: ?*TextBlock,
190 next: ?*TextBlock,
191
192 pub const empty = TextBlock{
193 .local_sym_index = 0,
194 .offset_table_index = undefined,
195 .prev = null,
196 .next = null,
197 };
198
199 /// Returns how much room there is to grow in virtual address space.
200 /// File offset relocation happens transparently, so it is not included in
201 /// this calculation.
202 fn capacity(self: TextBlock, elf_file: ElfFile) u64 {
203 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
204 if (self.next) |next| {
205 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
206 return next_sym.st_value - self_sym.st_value;
207 } else {
208 // We are the last block. The capacity is limited only by virtual address space.
209 return std.math.maxInt(u32) - self_sym.st_value;
210 }
211 }
212120
213 fn freeListEligible(self: TextBlock, elf_file: ElfFile) bool {121 return @fieldParentPtr(T, "base", base);
214 // No need to keep a free list node for the last block.122 }
215 const next = self.next orelse return false;
216 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
217 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
218 const cap = next_sym.st_value - self_sym.st_value;
219 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
220 if (cap <= ideal_cap) return false;
221 const surplus = cap - ideal_cap;
222 return surplus >= min_text_capacity;
223 }
224 };
225
226 pub const Export = struct {
227 sym_index: ?u32 = null,
228 };
229123
230 pub fn deinit(self: *ElfFile) void {124 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
231 self.sections.deinit(self.allocator);125 switch (base.tag) {
232 self.program_headers.deinit(self.allocator);126 .Elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
233 self.shstrtab.deinit(self.allocator);127 .C => {},
234 self.local_symbols.deinit(self.allocator);128 else => unreachable,
235 self.global_symbols.deinit(self.allocator);
236 self.global_symbol_free_list.deinit(self.allocator);
237 self.local_symbol_free_list.deinit(self.allocator);
238 self.offset_table_free_list.deinit(self.allocator);
239 self.text_block_free_list.deinit(self.allocator);
240 self.offset_table.deinit(self.allocator);
241 if (self.owns_file_handle) {
242 if (self.file) |f| f.close();
243 }129 }
244 }130 }
245131
246 pub fn makeExecutable(self: *ElfFile) !void {132 pub fn makeExecutable(base: *File) !void {
247 assert(self.owns_file_handle);133 switch (base.tag) {
248 if (self.file) |f| {134 .Elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
249 f.close();135 else => unreachable,
250 self.file = null;
251 }136 }
252 }137 }
253138
254 pub fn makeWritable(self: *ElfFile, dir: fs.Dir, sub_path: []const u8) !void {139 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
255 assert(self.owns_file_handle);140 switch (base.tag) {
256 if (self.file != null) return;141 .Elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
257 self.file = try dir.createFile(sub_path, .{142 .C => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
258 .truncate = false,143 else => unreachable,
259 .read = true,144 }
260 .mode = determineMode(self.options),
261 });
262 }145 }
263146
264 /// Returns end pos of collision, if any.147 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
265 fn detectAllocCollision(self: *ElfFile, start: u64, size: u64) ?u64 {148 switch (base.tag) {
266 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;149 .Elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
267 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);150 .C => {},
268 if (start < ehdr_size)151 else => unreachable,
269 return ehdr_size;
270
271 const end = start + satMul(size, alloc_num) / alloc_den;
272
273 if (self.shdr_table_offset) |off| {
274 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
275 const tight_size = self.sections.items.len * shdr_size;
276 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
277 const test_end = off + increased_size;
278 if (end > off and start < test_end) {
279 return test_end;
280 }
281 }152 }
153 }
282154
283 if (self.phdr_table_offset) |off| {155 pub fn deinit(base: *File) void {
284 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);156 switch (base.tag) {
285 const tight_size = self.sections.items.len * phdr_size;157 .Elf => @fieldParentPtr(Elf, "base", base).deinit(),
286 const increased_size = satMul(tight_size, alloc_num) / alloc_den;158 .C => @fieldParentPtr(C, "base", base).deinit(),
287 const test_end = off + increased_size;159 else => unreachable,
288 if (end > off and start < test_end) {
289 return test_end;
290 }
291 }160 }
161 }
292162
293 for (self.sections.items) |section| {163 pub fn destroy(base: *File) void {
294 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;164 switch (base.tag) {
295 const test_end = section.sh_offset + increased_size;165 .Elf => {
296 if (end > section.sh_offset and start < test_end) {166 const parent = @fieldParentPtr(Elf, "base", base);
297 return test_end;167 parent.deinit();
298 }168 parent.allocator.destroy(parent);
299 }169 },
300 for (self.program_headers.items) |program_header| {170 .C => {
301 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;171 const parent = @fieldParentPtr(C, "base", base);
302 const test_end = program_header.p_offset + increased_size;172 parent.deinit();
303 if (end > program_header.p_offset and start < test_end) {173 parent.allocator.destroy(parent);
304 return test_end;174 },
305 }175 else => unreachable,
306 }176 }
307 return null;
308 }177 }
309178
310 fn allocatedSize(self: *ElfFile, start: u64) u64 {179 pub fn flush(base: *File) !void {
311 var min_pos: u64 = std.math.maxInt(u64);180 try switch (base.tag) {
312 if (self.shdr_table_offset) |off| {181 .Elf => @fieldParentPtr(Elf, "base", base).flush(),
313 if (off > start and off < min_pos) min_pos = off;182 .C => @fieldParentPtr(C, "base", base).flush(),
314 }183 else => unreachable,
315 if (self.phdr_table_offset) |off| {184 };
316 if (off > start and off < min_pos) min_pos = off;
317 }
318 for (self.sections.items) |section| {
319 if (section.sh_offset <= start) continue;
320 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
321 }
322 for (self.program_headers.items) |program_header| {
323 if (program_header.p_offset <= start) continue;
324 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
325 }
326 return min_pos - start;
327 }185 }
328186
329 fn findFreeSpace(self: *ElfFile, object_size: u64, min_alignment: u16) u64 {187 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
330 var start: u64 = 0;188 switch (base.tag) {
331 while (self.detectAllocCollision(start, object_size)) |item_end| {189 .Elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
332 start = mem.alignForwardGeneric(u64, item_end, min_alignment);190 else => unreachable,
333 }191 }
334 return start;
335 }192 }
336193
337 fn makeString(self: *ElfFile, bytes: []const u8) !u32 {194 pub fn errorFlags(base: *File) ErrorFlags {
338 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);195 return switch (base.tag) {
339 const result = self.shstrtab.items.len;196 .Elf => @fieldParentPtr(Elf, "base", base).error_flags,
340 self.shstrtab.appendSliceAssumeCapacity(bytes);197 .C => return .{ .no_entry_point_found = false },
341 self.shstrtab.appendAssumeCapacity(0);198 else => unreachable,
342 return @intCast(u32, result);199 };
343 }200 }
344201
345 fn getString(self: *ElfFile, str_off: u32) []const u8 {202 pub fn options(base: *File) Options {
346 assert(str_off < self.shstrtab.items.len);203 return switch (base.tag) {
347 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));204 .Elf => @fieldParentPtr(Elf, "base", base).options,
205 .C => @fieldParentPtr(C, "base", base).options,
206 };
348 }207 }
349208
350 fn updateString(self: *ElfFile, old_str_off: u32, new_name: []const u8) !u32 {209 /// Must be called only after a successful call to `updateDecl`.
351 const existing_name = self.getString(old_str_off);210 pub fn updateDeclExports(
352 if (mem.eql(u8, existing_name, new_name)) {211 base: *File,
353 return old_str_off;212 module: *Module,
213 decl: *const Module.Decl,
214 exports: []const *Module.Export,
215 ) !void {
216 switch (base.tag) {
217 .Elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
218 .C => return {},
354 }219 }
355 return self.makeString(new_name);
356 }220 }
357221
358 pub fn populateMissingMetadata(self: *ElfFile) !void {222 pub const Tag = enum {
359 const small_ptr = switch (self.ptr_width) {223 Elf,
360 .p32 => true,224 C,
361 .p64 => false,225 };
362 };
363 const ptr_size: u8 = switch (self.ptr_width) {
364 .p32 => 4,
365 .p64 => 8,
366 };
367 if (self.phdr_load_re_index == null) {
368 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
369 const file_size = self.options.program_code_size_hint;
370 const p_align = 0x1000;
371 const off = self.findFreeSpace(file_size, p_align);
372 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
373 try self.program_headers.append(self.allocator, .{
374 .p_type = elf.PT_LOAD,
375 .p_offset = off,
376 .p_filesz = file_size,
377 .p_vaddr = default_entry_addr,
378 .p_paddr = default_entry_addr,
379 .p_memsz = file_size,
380 .p_align = p_align,
381 .p_flags = elf.PF_X | elf.PF_R,
382 });
383 self.entry_addr = null;
384 self.phdr_table_dirty = true;
385 }
386 if (self.phdr_got_index == null) {
387 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
388 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
389 // We really only need ptr alignment but since we are using PROGBITS, linux requires
390 // page align.
391 const p_align = 0x1000;
392 const off = self.findFreeSpace(file_size, p_align);
393 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
394 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
395 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
396 // else in virtual memory.
397 const default_got_addr = 0x4000000;
398 try self.program_headers.append(self.allocator, .{
399 .p_type = elf.PT_LOAD,
400 .p_offset = off,
401 .p_filesz = file_size,
402 .p_vaddr = default_got_addr,
403 .p_paddr = default_got_addr,
404 .p_memsz = file_size,
405 .p_align = p_align,
406 .p_flags = elf.PF_R,
407 });
408 self.phdr_table_dirty = true;
409 }
410 if (self.shstrtab_index == null) {
411 self.shstrtab_index = @intCast(u16, self.sections.items.len);
412 assert(self.shstrtab.items.len == 0);
413 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
414 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
415 //std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
416 try self.sections.append(self.allocator, .{
417 .sh_name = try self.makeString(".shstrtab"),
418 .sh_type = elf.SHT_STRTAB,
419 .sh_flags = 0,
420 .sh_addr = 0,
421 .sh_offset = off,
422 .sh_size = self.shstrtab.items.len,
423 .sh_link = 0,
424 .sh_info = 0,
425 .sh_addralign = 1,
426 .sh_entsize = 0,
427 });
428 self.shstrtab_dirty = true;
429 self.shdr_table_dirty = true;
430 }
431 if (self.text_section_index == null) {
432 self.text_section_index = @intCast(u16, self.sections.items.len);
433 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
434226
435 try self.sections.append(self.allocator, .{227 pub const ErrorFlags = struct {
436 .sh_name = try self.makeString(".text"),228 no_entry_point_found: bool = false,
437 .sh_type = elf.SHT_PROGBITS,229 };
438 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,230
439 .sh_addr = phdr.p_vaddr,231 pub const C = struct {
440 .sh_offset = phdr.p_offset,232 pub const base_tag: Tag = .C;
441 .sh_size = phdr.p_filesz,233 base: File = File{ .tag = base_tag },
442 .sh_link = 0,234
443 .sh_info = 0,235 allocator: *Allocator,
444 .sh_addralign = phdr.p_align,236 header: std.ArrayList(u8),
445 .sh_entsize = 0,237 constants: std.ArrayList(u8),
446 });238 main: std.ArrayList(u8),
447 self.shdr_table_dirty = true;239 file: ?fs.File,
240 options: Options,
241 called: std.StringHashMap(void),
242 need_stddef: bool = false,
243 need_stdint: bool = false,
244 need_noreturn: bool = false,
245 error_msg: *Module.ErrorMsg = undefined,
246
247 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {
248 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
249 return error.CGenFailure;
448 }250 }
449 if (self.got_section_index == null) {
450 self.got_section_index = @intCast(u16, self.sections.items.len);
451 const phdr = &self.program_headers.items[self.phdr_got_index.?];
452251
453 try self.sections.append(self.allocator, .{252 pub fn deinit(self: *File.C) void {
454 .sh_name = try self.makeString(".got"),253 self.main.deinit();
455 .sh_type = elf.SHT_PROGBITS,254 self.header.deinit();
456 .sh_flags = elf.SHF_ALLOC,255 self.constants.deinit();
457 .sh_addr = phdr.p_vaddr,256 self.called.deinit();
458 .sh_offset = phdr.p_offset,257 if (self.file) |f|
459 .sh_size = phdr.p_filesz,258 f.close();
460 .sh_link = 0,
461 .sh_info = 0,
462 .sh_addralign = phdr.p_align,
463 .sh_entsize = 0,
464 });
465 self.shdr_table_dirty = true;
466 }259 }
467 if (self.symtab_section_index == null) {260
468 self.symtab_section_index = @intCast(u16, self.sections.items.len);261 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
469 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);262 c_codegen.generate(self, decl) catch |err| {
470 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);263 if (err == error.CGenFailure) {
471 const file_size = self.options.symbol_count_hint * each_size;264 try module.failed_decls.put(module.gpa, decl, self.error_msg);
472 const off = self.findFreeSpace(file_size, min_align);265 }
473 //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });266 return err;
474267 };
475 try self.sections.append(self.allocator, .{
476 .sh_name = try self.makeString(".symtab"),
477 .sh_type = elf.SHT_SYMTAB,
478 .sh_flags = 0,
479 .sh_addr = 0,
480 .sh_offset = off,
481 .sh_size = file_size,
482 // The section header index of the associated string table.
483 .sh_link = self.shstrtab_index.?,
484 .sh_info = @intCast(u32, self.local_symbols.items.len),
485 .sh_addralign = min_align,
486 .sh_entsize = each_size,
487 });
488 self.shdr_table_dirty = true;
489 try self.writeSymbol(0);
490 }268 }
491 const shsize: u64 = switch (self.ptr_width) {269
492 .p32 => @sizeOf(elf.Elf32_Shdr),270 pub fn flush(self: *File.C) !void {
493 .p64 => @sizeOf(elf.Elf64_Shdr),271 const writer = self.file.?.writer();
494 };272 try writer.writeAll(@embedFile("cbe.h"));
495 const shalign: u16 = switch (self.ptr_width) {273 var includes = false;
496 .p32 => @alignOf(elf.Elf32_Shdr),274 if (self.need_stddef) {
497 .p64 => @alignOf(elf.Elf64_Shdr),275 try writer.writeAll("#include <stddef.h>\n");
498 };276 includes = true;
499 if (self.shdr_table_offset == null) {277 }
500 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);278 if (self.need_stdint) {
501 self.shdr_table_dirty = true;279 try writer.writeAll("#include <stdint.h>\n");
280 includes = true;
281 }
282 if (includes) {
283 try writer.writeByte('\n');
284 }
285 if (self.header.items.len > 0) {
286 try writer.print("{}\n", .{self.header.items});
287 }
288 if (self.constants.items.len > 0) {
289 try writer.print("{}\n", .{self.constants.items});
290 }
291 if (self.main.items.len > 1) {
292 const last_two = self.main.items[self.main.items.len - 2 ..];
293 if (std.mem.eql(u8, last_two, "\n\n")) {
294 self.main.items.len -= 1;
295 }
296 }
297 try writer.writeAll(self.main.items);
298 self.file.?.close();
299 self.file = null;
502 }300 }
503 const phsize: u64 = switch (self.ptr_width) {301 };
504 .p32 => @sizeOf(elf.Elf32_Phdr),302
505 .p64 => @sizeOf(elf.Elf64_Phdr),303 pub const Elf = struct {
304 pub const base_tag: Tag = .Elf;
305 base: File = File{ .tag = base_tag },
306
307 allocator: *Allocator,
308 file: ?fs.File,
309 owns_file_handle: bool,
310 options: Options,
311 ptr_width: enum { p32, p64 },
312
313 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
314 /// Same order as in the file.
315 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
316 shdr_table_offset: ?u64 = null,
317
318 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
319 /// Same order as in the file.
320 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
321 phdr_table_offset: ?u64 = null,
322 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
323 phdr_load_re_index: ?u16 = null,
324 /// The index into the program headers of the global offset table.
325 /// It needs PT_LOAD and Read flags.
326 phdr_got_index: ?u16 = null,
327 entry_addr: ?u64 = null,
328
329 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
330 shstrtab_index: ?u16 = null,
331
332 text_section_index: ?u16 = null,
333 symtab_section_index: ?u16 = null,
334 got_section_index: ?u16 = null,
335
336 /// The same order as in the file. ELF requires global symbols to all be after the
337 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
338 /// write them at the end. These are only the local symbols. The length of this array
339 /// is the value used for sh_info in the .symtab section.
340 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
341 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
342
343 local_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
344 global_symbol_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
345 offset_table_free_list: std.ArrayListUnmanaged(u32) = std.ArrayListUnmanaged(u32){},
346
347 /// Same order as in the file. The value is the absolute vaddr value.
348 /// If the vaddr of the executable program header changes, the entire
349 /// offset table needs to be rewritten.
350 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
351
352 phdr_table_dirty: bool = false,
353 shdr_table_dirty: bool = false,
354 shstrtab_dirty: bool = false,
355 offset_table_count_dirty: bool = false,
356
357 error_flags: ErrorFlags = ErrorFlags{},
358
359 /// A list of text blocks that have surplus capacity. This list can have false
360 /// positives, as functions grow and shrink over time, only sometimes being added
361 /// or removed from the freelist.
362 ///
363 /// A text block has surplus capacity when its overcapacity value is greater than
364 /// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
365 /// much extra capacity, that we could fit a small new symbol in it, itself with
366 /// ideal_capacity or more.
367 ///
368 /// Ideal capacity is defined by size * alloc_num / alloc_den.
369 ///
370 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
371 /// overcapacity can be negative. A simple way to have negative overcapacity is to
372 /// allocate a fresh text block, which will have ideal capacity, and then grow it
373 /// by 1 byte. It will then have -1 overcapacity.
374 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
375 last_text_block: ?*TextBlock = null,
376
377 /// `alloc_num / alloc_den` is the factor of padding when allocating.
378 const alloc_num = 4;
379 const alloc_den = 3;
380
381 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
382 /// it as a possible place to put new symbols, it must have enough room for this many bytes
383 /// (plus extra for reserved capacity).
384 const minimum_text_block_size = 64;
385 const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
386
387 pub const TextBlock = struct {
388 /// Each decl always gets a local symbol with the fully qualified name.
389 /// The vaddr and size are found here directly.
390 /// The file offset is found by computing the vaddr offset from the section vaddr
391 /// the symbol references, and adding that to the file offset of the section.
392 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
393 /// offset table entry.
394 local_sym_index: u32,
395 /// This field is undefined for symbols with size = 0.
396 offset_table_index: u32,
397 /// Points to the previous and next neighbors, based on the `text_offset`.
398 /// This can be used to find, for example, the capacity of this `TextBlock`.
399 prev: ?*TextBlock,
400 next: ?*TextBlock,
401
402 pub const empty = TextBlock{
403 .local_sym_index = 0,
404 .offset_table_index = undefined,
405 .prev = null,
406 .next = null,
407 };
408
409 /// Returns how much room there is to grow in virtual address space.
410 /// File offset relocation happens transparently, so it is not included in
411 /// this calculation.
412 fn capacity(self: TextBlock, elf_file: Elf) u64 {
413 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
414 if (self.next) |next| {
415 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
416 return next_sym.st_value - self_sym.st_value;
417 } else {
418 // We are the last block. The capacity is limited only by virtual address space.
419 return std.math.maxInt(u32) - self_sym.st_value;
420 }
421 }
422
423 fn freeListEligible(self: TextBlock, elf_file: Elf) bool {
424 // No need to keep a free list node for the last block.
425 const next = self.next orelse return false;
426 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
427 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
428 const cap = next_sym.st_value - self_sym.st_value;
429 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;
430 if (cap <= ideal_cap) return false;
431 const surplus = cap - ideal_cap;
432 return surplus >= min_text_capacity;
433 }
506 };434 };
507 const phalign: u16 = switch (self.ptr_width) {435
508 .p32 => @alignOf(elf.Elf32_Phdr),436 pub const Export = struct {
509 .p64 => @alignOf(elf.Elf64_Phdr),437 sym_index: ?u32 = null,
510 };438 };
511 if (self.phdr_table_offset == null) {439
512 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);440 pub fn deinit(self: *Elf) void {
513 self.phdr_table_dirty = true;441 self.sections.deinit(self.allocator);
514 }442 self.program_headers.deinit(self.allocator);
515 {443 self.shstrtab.deinit(self.allocator);
516 // Iterate over symbols, populating free_list and last_text_block.444 self.local_symbols.deinit(self.allocator);
517 if (self.local_symbols.items.len != 1) {445 self.global_symbols.deinit(self.allocator);
518 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");446 self.global_symbol_free_list.deinit(self.allocator);
447 self.local_symbol_free_list.deinit(self.allocator);
448 self.offset_table_free_list.deinit(self.allocator);
449 self.text_block_free_list.deinit(self.allocator);
450 self.offset_table.deinit(self.allocator);
451 if (self.owns_file_handle) {
452 if (self.file) |f| f.close();
519 }453 }
520 // We are starting with an empty file. The default values are correct, null and empty list.
521 }454 }
522 }
523455
524 /// Commit pending changes and write headers.456 pub fn makeExecutable(self: *Elf) !void {
525 pub fn flush(self: *ElfFile) !void {457 assert(self.owns_file_handle);
526 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();458 if (self.file) |f| {
459 f.close();
460 self.file = null;
461 }
462 }
527463
528 // Unfortunately these have to be buffered and done at the end because ELF does not allow464 pub fn makeWritable(self: *Elf, dir: fs.Dir, sub_path: []const u8) !void {
529 // mixing local and global symbols within a symbol table.465 assert(self.owns_file_handle);
530 try self.writeAllGlobalSymbols();466 if (self.file != null) return;
467 self.file = try dir.createFile(sub_path, .{
468 .truncate = false,
469 .read = true,
470 .mode = determineMode(self.options),
471 });
472 }
531473
532 if (self.phdr_table_dirty) {474 /// Returns end pos of collision, if any.
533 const phsize: u64 = switch (self.ptr_width) {475 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
534 .p32 => @sizeOf(elf.Elf32_Phdr),476 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
535 .p64 => @sizeOf(elf.Elf64_Phdr),477 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
536 };478 if (start < ehdr_size)
537 const phalign: u16 = switch (self.ptr_width) {479 return ehdr_size;
538 .p32 => @alignOf(elf.Elf32_Phdr),480
539 .p64 => @alignOf(elf.Elf64_Phdr),481 const end = start + satMul(size, alloc_num) / alloc_den;
540 };482
541 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);483 if (self.shdr_table_offset) |off| {
542 const needed_size = self.program_headers.items.len * phsize;484 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
485 const tight_size = self.sections.items.len * shdr_size;
486 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
487 const test_end = off + increased_size;
488 if (end > off and start < test_end) {
489 return test_end;
490 }
491 }
543492
544 if (needed_size > allocated_size) {493 if (self.phdr_table_offset) |off| {
545 self.phdr_table_offset = null; // free the space494 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
546 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);495 const tight_size = self.sections.items.len * phdr_size;
496 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
497 const test_end = off + increased_size;
498 if (end > off and start < test_end) {
499 return test_end;
500 }
547 }501 }
548502
549 switch (self.ptr_width) {503 for (self.sections.items) |section| {
550 .p32 => {504 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
551 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);505 const test_end = section.sh_offset + increased_size;
552 defer self.allocator.free(buf);506 if (end > section.sh_offset and start < test_end) {
507 return test_end;
508 }
509 }
510 for (self.program_headers.items) |program_header| {
511 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
512 const test_end = program_header.p_offset + increased_size;
513 if (end > program_header.p_offset and start < test_end) {
514 return test_end;
515 }
516 }
517 return null;
518 }
553519
554 for (buf) |*phdr, i| {520 fn allocatedSize(self: *Elf, start: u64) u64 {
555 phdr.* = progHeaderTo32(self.program_headers.items[i]);521 var min_pos: u64 = std.math.maxInt(u64);
556 if (foreign_endian) {522 if (self.shdr_table_offset) |off| {
557 bswapAllFields(elf.Elf32_Phdr, phdr);523 if (off > start and off < min_pos) min_pos = off;
558 }524 }
559 }525 if (self.phdr_table_offset) |off| {
560 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);526 if (off > start and off < min_pos) min_pos = off;
561 },527 }
562 .p64 => {528 for (self.sections.items) |section| {
563 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);529 if (section.sh_offset <= start) continue;
564 defer self.allocator.free(buf);530 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
531 }
532 for (self.program_headers.items) |program_header| {
533 if (program_header.p_offset <= start) continue;
534 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
535 }
536 return min_pos - start;
537 }
565538
566 for (buf) |*phdr, i| {539 fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
567 phdr.* = self.program_headers.items[i];540 var start: u64 = 0;
568 if (foreign_endian) {541 while (self.detectAllocCollision(start, object_size)) |item_end| {
569 bswapAllFields(elf.Elf64_Phdr, phdr);542 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
570 }
571 }
572 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
573 },
574 }543 }
575 self.phdr_table_dirty = false;544 return start;
576 }545 }
577546
578 {547 fn makeString(self: *Elf, bytes: []const u8) !u32 {
579 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];548 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
580 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {549 const result = self.shstrtab.items.len;
581 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);550 self.shstrtab.appendSliceAssumeCapacity(bytes);
582 const needed_size = self.shstrtab.items.len;551 self.shstrtab.appendAssumeCapacity(0);
552 return @intCast(u32, result);
553 }
583554
584 if (needed_size > allocated_size) {555 fn getString(self: *Elf, str_off: u32) []const u8 {
585 shstrtab_sect.sh_size = 0; // free the space556 assert(str_off < self.shstrtab.items.len);
586 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);557 return mem.spanZ(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off));
587 }558 }
588 shstrtab_sect.sh_size = needed_size;
589 //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
590559
591 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);560 fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
592 if (!self.shdr_table_dirty) {561 const existing_name = self.getString(old_str_off);
593 // Then it won't get written with the others and we need to do it.562 if (mem.eql(u8, existing_name, new_name)) {
594 try self.writeSectHeader(self.shstrtab_index.?);563 return old_str_off;
595 }
596 self.shstrtab_dirty = false;
597 }564 }
565 return self.makeString(new_name);
598 }566 }
599 if (self.shdr_table_dirty) {567
568 pub fn populateMissingMetadata(self: *Elf) !void {
569 const small_ptr = switch (self.ptr_width) {
570 .p32 => true,
571 .p64 => false,
572 };
573 const ptr_size: u8 = switch (self.ptr_width) {
574 .p32 => 4,
575 .p64 => 8,
576 };
577 if (self.phdr_load_re_index == null) {
578 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
579 const file_size = self.options.program_code_size_hint;
580 const p_align = 0x1000;
581 const off = self.findFreeSpace(file_size, p_align);
582 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
583 try self.program_headers.append(self.allocator, .{
584 .p_type = elf.PT_LOAD,
585 .p_offset = off,
586 .p_filesz = file_size,
587 .p_vaddr = default_entry_addr,
588 .p_paddr = default_entry_addr,
589 .p_memsz = file_size,
590 .p_align = p_align,
591 .p_flags = elf.PF_X | elf.PF_R,
592 });
593 self.entry_addr = null;
594 self.phdr_table_dirty = true;
595 }
596 if (self.phdr_got_index == null) {
597 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
598 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
599 // We really only need ptr alignment but since we are using PROGBITS, linux requires
600 // page align.
601 const p_align = 0x1000;
602 const off = self.findFreeSpace(file_size, p_align);
603 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
604 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
605 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
606 // else in virtual memory.
607 const default_got_addr = 0x4000000;
608 try self.program_headers.append(self.allocator, .{
609 .p_type = elf.PT_LOAD,
610 .p_offset = off,
611 .p_filesz = file_size,
612 .p_vaddr = default_got_addr,
613 .p_paddr = default_got_addr,
614 .p_memsz = file_size,
615 .p_align = p_align,
616 .p_flags = elf.PF_R,
617 });
618 self.phdr_table_dirty = true;
619 }
620 if (self.shstrtab_index == null) {
621 self.shstrtab_index = @intCast(u16, self.sections.items.len);
622 assert(self.shstrtab.items.len == 0);
623 try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
624 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
625 std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
626 try self.sections.append(self.allocator, .{
627 .sh_name = try self.makeString(".shstrtab"),
628 .sh_type = elf.SHT_STRTAB,
629 .sh_flags = 0,
630 .sh_addr = 0,
631 .sh_offset = off,
632 .sh_size = self.shstrtab.items.len,
633 .sh_link = 0,
634 .sh_info = 0,
635 .sh_addralign = 1,
636 .sh_entsize = 0,
637 });
638 self.shstrtab_dirty = true;
639 self.shdr_table_dirty = true;
640 }
641 if (self.text_section_index == null) {
642 self.text_section_index = @intCast(u16, self.sections.items.len);
643 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
644
645 try self.sections.append(self.allocator, .{
646 .sh_name = try self.makeString(".text"),
647 .sh_type = elf.SHT_PROGBITS,
648 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
649 .sh_addr = phdr.p_vaddr,
650 .sh_offset = phdr.p_offset,
651 .sh_size = phdr.p_filesz,
652 .sh_link = 0,
653 .sh_info = 0,
654 .sh_addralign = phdr.p_align,
655 .sh_entsize = 0,
656 });
657 self.shdr_table_dirty = true;
658 }
659 if (self.got_section_index == null) {
660 self.got_section_index = @intCast(u16, self.sections.items.len);
661 const phdr = &self.program_headers.items[self.phdr_got_index.?];
662
663 try self.sections.append(self.allocator, .{
664 .sh_name = try self.makeString(".got"),
665 .sh_type = elf.SHT_PROGBITS,
666 .sh_flags = elf.SHF_ALLOC,
667 .sh_addr = phdr.p_vaddr,
668 .sh_offset = phdr.p_offset,
669 .sh_size = phdr.p_filesz,
670 .sh_link = 0,
671 .sh_info = 0,
672 .sh_addralign = phdr.p_align,
673 .sh_entsize = 0,
674 });
675 self.shdr_table_dirty = true;
676 }
677 if (self.symtab_section_index == null) {
678 self.symtab_section_index = @intCast(u16, self.sections.items.len);
679 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
680 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
681 const file_size = self.options.symbol_count_hint * each_size;
682 const off = self.findFreeSpace(file_size, min_align);
683 std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
684
685 try self.sections.append(self.allocator, .{
686 .sh_name = try self.makeString(".symtab"),
687 .sh_type = elf.SHT_SYMTAB,
688 .sh_flags = 0,
689 .sh_addr = 0,
690 .sh_offset = off,
691 .sh_size = file_size,
692 // The section header index of the associated string table.
693 .sh_link = self.shstrtab_index.?,
694 .sh_info = @intCast(u32, self.local_symbols.items.len),
695 .sh_addralign = min_align,
696 .sh_entsize = each_size,
697 });
698 self.shdr_table_dirty = true;
699 try self.writeSymbol(0);
700 }
600 const shsize: u64 = switch (self.ptr_width) {701 const shsize: u64 = switch (self.ptr_width) {
601 .p32 => @sizeOf(elf.Elf32_Shdr),702 .p32 => @sizeOf(elf.Elf32_Shdr),
602 .p64 => @sizeOf(elf.Elf64_Shdr),703 .p64 => @sizeOf(elf.Elf64_Shdr),
...@@ -605,763 +706,874 @@ pub const ElfFile = struct {...@@ -605,763 +706,874 @@ pub const ElfFile = struct {
605 .p32 => @alignOf(elf.Elf32_Shdr),706 .p32 => @alignOf(elf.Elf32_Shdr),
606 .p64 => @alignOf(elf.Elf64_Shdr),707 .p64 => @alignOf(elf.Elf64_Shdr),
607 };708 };
608 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);709 if (self.shdr_table_offset == null) {
609 const needed_size = self.sections.items.len * shsize;710 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
610711 self.shdr_table_dirty = true;
611 if (needed_size > allocated_size) {712 }
612 self.shdr_table_offset = null; // free the space713 const phsize: u64 = switch (self.ptr_width) {
613 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);714 .p32 => @sizeOf(elf.Elf32_Phdr),
715 .p64 => @sizeOf(elf.Elf64_Phdr),
716 };
717 const phalign: u16 = switch (self.ptr_width) {
718 .p32 => @alignOf(elf.Elf32_Phdr),
719 .p64 => @alignOf(elf.Elf64_Phdr),
720 };
721 if (self.phdr_table_offset == null) {
722 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
723 self.phdr_table_dirty = true;
724 }
725 {
726 // Iterate over symbols, populating free_list and last_text_block.
727 if (self.local_symbols.items.len != 1) {
728 @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
729 }
730 // We are starting with an empty file. The default values are correct, null and empty list.
614 }731 }
732 }
615733
616 switch (self.ptr_width) {734 /// Commit pending changes and write headers.
617 .p32 => {735 pub fn flush(self: *Elf) !void {
618 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);736 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
619 defer self.allocator.free(buf);
620737
621 for (buf) |*shdr, i| {738 // Unfortunately these have to be buffered and done at the end because ELF does not allow
622 shdr.* = sectHeaderTo32(self.sections.items[i]);739 // mixing local and global symbols within a symbol table.
623 if (foreign_endian) {740 try self.writeAllGlobalSymbols();
624 bswapAllFields(elf.Elf32_Shdr, shdr);741
742 if (self.phdr_table_dirty) {
743 const phsize: u64 = switch (self.ptr_width) {
744 .p32 => @sizeOf(elf.Elf32_Phdr),
745 .p64 => @sizeOf(elf.Elf64_Phdr),
746 };
747 const phalign: u16 = switch (self.ptr_width) {
748 .p32 => @alignOf(elf.Elf32_Phdr),
749 .p64 => @alignOf(elf.Elf64_Phdr),
750 };
751 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
752 const needed_size = self.program_headers.items.len * phsize;
753
754 if (needed_size > allocated_size) {
755 self.phdr_table_offset = null; // free the space
756 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
757 }
758
759 switch (self.ptr_width) {
760 .p32 => {
761 const buf = try self.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
762 defer self.allocator.free(buf);
763
764 for (buf) |*phdr, i| {
765 phdr.* = progHeaderTo32(self.program_headers.items[i]);
766 if (foreign_endian) {
767 bswapAllFields(elf.Elf32_Phdr, phdr);
768 }
625 }769 }
770 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
771 },
772 .p64 => {
773 const buf = try self.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
774 defer self.allocator.free(buf);
775
776 for (buf) |*phdr, i| {
777 phdr.* = self.program_headers.items[i];
778 if (foreign_endian) {
779 bswapAllFields(elf.Elf64_Phdr, phdr);
780 }
781 }
782 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
783 },
784 }
785 self.phdr_table_dirty = false;
786 }
787
788 {
789 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
790 if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
791 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
792 const needed_size = self.shstrtab.items.len;
793
794 if (needed_size > allocated_size) {
795 shstrtab_sect.sh_size = 0; // free the space
796 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
626 }797 }
627 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);798 shstrtab_sect.sh_size = needed_size;
628 },799 std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
629 .p64 => {
630 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
631 defer self.allocator.free(buf);
632800
633 for (buf) |*shdr, i| {801 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
634 shdr.* = self.sections.items[i];802 if (!self.shdr_table_dirty) {
635 //std.debug.warn("writing section {}\n", .{shdr.*});803 // Then it won't get written with the others and we need to do it.
636 if (foreign_endian) {804 try self.writeSectHeader(self.shstrtab_index.?);
637 bswapAllFields(elf.Elf64_Shdr, shdr);
638 }
639 }805 }
640 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);806 self.shstrtab_dirty = false;
641 },807 }
642 }808 }
643 self.shdr_table_dirty = false;809 if (self.shdr_table_dirty) {
644 }810 const shsize: u64 = switch (self.ptr_width) {
645 if (self.entry_addr == null and self.options.output_mode == .Exe) {811 .p32 => @sizeOf(elf.Elf32_Shdr),
646 self.error_flags.no_entry_point_found = true;812 .p64 => @sizeOf(elf.Elf64_Shdr),
647 } else {813 };
648 self.error_flags.no_entry_point_found = false;814 const shalign: u16 = switch (self.ptr_width) {
649 try self.writeElfHeader();815 .p32 => @alignOf(elf.Elf32_Shdr),
650 }816 .p64 => @alignOf(elf.Elf64_Shdr),
817 };
818 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
819 const needed_size = self.sections.items.len * shsize;
651820
652 // The point of flush() is to commit changes, so nothing should be dirty after this.821 if (needed_size > allocated_size) {
653 assert(!self.phdr_table_dirty);822 self.shdr_table_offset = null; // free the space
654 assert(!self.shdr_table_dirty);823 self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
655 assert(!self.shstrtab_dirty);824 }
656 assert(!self.offset_table_count_dirty);
657 const syms_sect = &self.sections.items[self.symtab_section_index.?];
658 assert(syms_sect.sh_info == self.local_symbols.items.len);
659 }
660825
661 fn writeElfHeader(self: *ElfFile) !void {826 switch (self.ptr_width) {
662 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;827 .p32 => {
828 const buf = try self.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
829 defer self.allocator.free(buf);
663830
664 var index: usize = 0;831 for (buf) |*shdr, i| {
665 hdr_buf[0..4].* = "\x7fELF".*;832 shdr.* = sectHeaderTo32(self.sections.items[i]);
666 index += 4;833 if (foreign_endian) {
834 bswapAllFields(elf.Elf32_Shdr, shdr);
835 }
836 }
837 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
838 },
839 .p64 => {
840 const buf = try self.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
841 defer self.allocator.free(buf);
842
843 for (buf) |*shdr, i| {
844 shdr.* = self.sections.items[i];
845 std.log.debug(.link, "writing section {}\n", .{shdr.*});
846 if (foreign_endian) {
847 bswapAllFields(elf.Elf64_Shdr, shdr);
848 }
849 }
850 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
851 },
852 }
853 self.shdr_table_dirty = false;
854 }
855 if (self.entry_addr == null and self.options.output_mode == .Exe) {
856 std.log.debug(.link, "no_entry_point_found = true\n", .{});
857 self.error_flags.no_entry_point_found = true;
858 } else {
859 self.error_flags.no_entry_point_found = false;
860 try self.writeElfHeader();
861 }
667862
668 hdr_buf[index] = switch (self.ptr_width) {863 // The point of flush() is to commit changes, so nothing should be dirty after this.
669 .p32 => elf.ELFCLASS32,864 assert(!self.phdr_table_dirty);
670 .p64 => elf.ELFCLASS64,865 assert(!self.shdr_table_dirty);
671 };866 assert(!self.shstrtab_dirty);
672 index += 1;867 assert(!self.offset_table_count_dirty);
868 const syms_sect = &self.sections.items[self.symtab_section_index.?];
869 assert(syms_sect.sh_info == self.local_symbols.items.len);
870 }
673871
674 const endian = self.options.target.cpu.arch.endian();872 fn writeElfHeader(self: *Elf) !void {
675 hdr_buf[index] = switch (endian) {873 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
676 .Little => elf.ELFDATA2LSB,
677 .Big => elf.ELFDATA2MSB,
678 };
679 index += 1;
680874
681 hdr_buf[index] = 1; // ELF version875 var index: usize = 0;
682 index += 1;876 hdr_buf[0..4].* = "\x7fELF".*;
877 index += 4;
683878
684 // OS ABI, often set to 0 regardless of target platform879 hdr_buf[index] = switch (self.ptr_width) {
685 // ABI Version, possibly used by glibc but not by static executables880 .p32 => elf.ELFCLASS32,
686 // padding881 .p64 => elf.ELFCLASS64,
687 mem.set(u8, hdr_buf[index..][0..9], 0);882 };
688 index += 9;883 index += 1;
689884
690 assert(index == 16);885 const endian = self.options.target.cpu.arch.endian();
886 hdr_buf[index] = switch (endian) {
887 .Little => elf.ELFDATA2LSB,
888 .Big => elf.ELFDATA2MSB,
889 };
890 index += 1;
691891
692 const elf_type = switch (self.options.output_mode) {892 hdr_buf[index] = 1; // ELF version
693 .Exe => elf.ET.EXEC,893 index += 1;
694 .Obj => elf.ET.REL,
695 .Lib => switch (self.options.link_mode) {
696 .Static => elf.ET.REL,
697 .Dynamic => elf.ET.DYN,
698 },
699 };
700 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
701 index += 2;
702894
703 const machine = self.options.target.cpu.arch.toElfMachine();895 // OS ABI, often set to 0 regardless of target platform
704 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);896 // ABI Version, possibly used by glibc but not by static executables
705 index += 2;897 // padding
898 mem.set(u8, hdr_buf[index..][0..9], 0);
899 index += 9;
706900
707 // ELF Version, again901 assert(index == 16);
708 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
709 index += 4;
710902
711 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;903 const elf_type = switch (self.options.output_mode) {
904 .Exe => elf.ET.EXEC,
905 .Obj => elf.ET.REL,
906 .Lib => switch (self.options.link_mode) {
907 .Static => elf.ET.REL,
908 .Dynamic => elf.ET.DYN,
909 },
910 };
911 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
912 index += 2;
712913
713 switch (self.ptr_width) {914 const machine = self.options.target.cpu.arch.toElfMachine();
714 .p32 => {915 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
715 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);916 index += 2;
716 index += 4;
717917
718 // e_phoff918 // ELF Version, again
719 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);919 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
720 index += 4;920 index += 4;
721921
722 // e_shoff922 const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
723 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
724 index += 4;
725 },
726 .p64 => {
727 // e_entry
728 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
729 index += 8;
730
731 // e_phoff
732 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
733 index += 8;
734
735 // e_shoff
736 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
737 index += 8;
738 },
739 }
740923
741 const e_flags = 0;924 switch (self.ptr_width) {
742 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);925 .p32 => {
743 index += 4;926 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
927 index += 4;
744928
745 const e_ehsize: u16 = switch (self.ptr_width) {929 // e_phoff
746 .p32 => @sizeOf(elf.Elf32_Ehdr),930 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
747 .p64 => @sizeOf(elf.Elf64_Ehdr),931 index += 4;
748 };
749 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
750 index += 2;
751932
752 const e_phentsize: u16 = switch (self.ptr_width) {933 // e_shoff
753 .p32 => @sizeOf(elf.Elf32_Phdr),934 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
754 .p64 => @sizeOf(elf.Elf64_Phdr),935 index += 4;
755 };936 },
756 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);937 .p64 => {
757 index += 2;938 // e_entry
939 mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
940 index += 8;
758941
759 const e_phnum = @intCast(u16, self.program_headers.items.len);942 // e_phoff
760 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);943 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
761 index += 2;944 index += 8;
762945
763 const e_shentsize: u16 = switch (self.ptr_width) {946 // e_shoff
764 .p32 => @sizeOf(elf.Elf32_Shdr),947 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
765 .p64 => @sizeOf(elf.Elf64_Shdr),948 index += 8;
766 };949 },
767 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);950 }
768 index += 2;
769951
770 const e_shnum = @intCast(u16, self.sections.items.len);952 const e_flags = 0;
771 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);953 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
772 index += 2;954 index += 4;
773955
774 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);956 const e_ehsize: u16 = switch (self.ptr_width) {
775 index += 2;957 .p32 => @sizeOf(elf.Elf32_Ehdr),
958 .p64 => @sizeOf(elf.Elf64_Ehdr),
959 };
960 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
961 index += 2;
776962
777 assert(index == e_ehsize);963 const e_phentsize: u16 = switch (self.ptr_width) {
964 .p32 => @sizeOf(elf.Elf32_Phdr),
965 .p64 => @sizeOf(elf.Elf64_Phdr),
966 };
967 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
968 index += 2;
778969
779 try self.file.?.pwriteAll(hdr_buf[0..index], 0);970 const e_phnum = @intCast(u16, self.program_headers.items.len);
780 }971 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
972 index += 2;
781973
782 fn freeTextBlock(self: *ElfFile, text_block: *TextBlock) void {974 const e_shentsize: u16 = switch (self.ptr_width) {
783 var already_have_free_list_node = false;975 .p32 => @sizeOf(elf.Elf32_Shdr),
784 {976 .p64 => @sizeOf(elf.Elf64_Shdr),
785 var i: usize = 0;977 };
786 while (i < self.text_block_free_list.items.len) {978 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
787 if (self.text_block_free_list.items[i] == text_block) {979 index += 2;
788 _ = self.text_block_free_list.swapRemove(i);
789 continue;
790 }
791 if (self.text_block_free_list.items[i] == text_block.prev) {
792 already_have_free_list_node = true;
793 }
794 i += 1;
795 }
796 }
797980
798 if (self.last_text_block == text_block) {981 const e_shnum = @intCast(u16, self.sections.items.len);
799 // TODO shrink the .text section size here982 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
800 self.last_text_block = text_block.prev;983 index += 2;
801 }
802984
803 if (text_block.prev) |prev| {985 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
804 prev.next = text_block.next;986 index += 2;
805987
806 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {988 assert(index == e_ehsize);
807 // The free list is heuristics, it doesn't have to be perfect, so we can
808 // ignore the OOM here.
809 self.text_block_free_list.append(self.allocator, prev) catch {};
810 }
811 } else {
812 text_block.prev = null;
813 }
814989
815 if (text_block.next) |next| {990 try self.file.?.pwriteAll(hdr_buf[0..index], 0);
816 next.prev = text_block.prev;
817 } else {
818 text_block.next = null;
819 }991 }
820 }
821992
822 fn shrinkTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64) void {993 fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
823 // TODO check the new capacity, and if it crosses the size threshold into a big enough994 var already_have_free_list_node = false;
824 // capacity, insert a free list node for it.995 {
825 }996 var i: usize = 0;
826997 while (i < self.text_block_free_list.items.len) {
827 fn growTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {998 if (self.text_block_free_list.items[i] == text_block) {
828 const sym = self.local_symbols.items[text_block.local_sym_index];
829 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
830 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
831 if (!need_realloc) return sym.st_value;
832 return self.allocateTextBlock(text_block, new_block_size, alignment);
833 }
834
835 fn allocateTextBlock(self: *ElfFile, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
836 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
837 const shdr = &self.sections.items[self.text_section_index.?];
838 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
839
840 // We use these to indicate our intention to update metadata, placing the new block,
841 // and possibly removing a free list node.
842 // It would be simpler to do it inside the for loop below, but that would cause a
843 // problem if an error was returned later in the function. So this action
844 // is actually carried out at the end of the function, when errors are no longer possible.
845 var block_placement: ?*TextBlock = null;
846 var free_list_removal: ?usize = null;
847
848 // First we look for an appropriately sized free list node.
849 // The list is unordered. We'll just take the first thing that works.
850 const vaddr = blk: {
851 var i: usize = 0;
852 while (i < self.text_block_free_list.items.len) {
853 const big_block = self.text_block_free_list.items[i];
854 // We now have a pointer to a live text block that has too much capacity.
855 // Is it enough that we could fit this new text block?
856 const sym = self.local_symbols.items[big_block.local_sym_index];
857 const capacity = big_block.capacity(self.*);
858 const ideal_capacity = capacity * alloc_num / alloc_den;
859 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
860 const capacity_end_vaddr = sym.st_value + capacity;
861 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
862 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
863 if (new_start_vaddr < ideal_capacity_end_vaddr) {
864 // Additional bookkeeping here to notice if this free list node
865 // should be deleted because the block that it points to has grown to take up
866 // more of the extra capacity.
867 if (!big_block.freeListEligible(self.*)) {
868 _ = self.text_block_free_list.swapRemove(i);999 _ = self.text_block_free_list.swapRemove(i);
869 } else {1000 continue;
870 i += 1;
871 }1001 }
872 continue;1002 if (self.text_block_free_list.items[i] == text_block.prev) {
873 }1003 already_have_free_list_node = true;
874 // At this point we know that we will place the new block here. But the1004 }
875 // remaining question is whether there is still yet enough capacity left1005 i += 1;
876 // over for there to still be a free list node.
877 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
878 const keep_free_list_node = remaining_capacity >= min_text_capacity;
879
880 // Set up the metadata to be updated, after errors are no longer possible.
881 block_placement = big_block;
882 if (!keep_free_list_node) {
883 free_list_removal = i;
884 }1006 }
885 break :blk new_start_vaddr;
886 } else if (self.last_text_block) |last| {
887 const sym = self.local_symbols.items[last.local_sym_index];
888 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
889 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
890 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
891 // Set up the metadata to be updated, after errors are no longer possible.
892 block_placement = last;
893 break :blk new_start_vaddr;
894 } else {
895 break :blk phdr.p_vaddr;
896 }1007 }
897 };
8981008
899 const expand_text_section = block_placement == null or block_placement.?.next == null;1009 if (self.last_text_block == text_block) {
900 if (expand_text_section) {1010 // TODO shrink the .text section size here
901 const text_capacity = self.allocatedSize(shdr.sh_offset);1011 self.last_text_block = text_block.prev;
902 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
903 if (needed_size > text_capacity) {
904 // Must move the entire text section.
905 const new_offset = self.findFreeSpace(needed_size, 0x1000);
906 const text_size = if (self.last_text_block) |last| blk: {
907 const sym = self.local_symbols.items[last.local_sym_index];
908 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
909 } else 0;
910 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
911 if (amt != text_size) return error.InputOutput;
912 shdr.sh_offset = new_offset;
913 phdr.p_offset = new_offset;
914 }1012 }
915 self.last_text_block = text_block;
9161013
917 shdr.sh_size = needed_size;1014 if (text_block.prev) |prev| {
918 phdr.p_memsz = needed_size;1015 prev.next = text_block.next;
919 phdr.p_filesz = needed_size;
9201016
921 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty1017 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
922 self.shdr_table_dirty = true; // TODO look into making only the one section dirty1018 // The free list is heuristics, it doesn't have to be perfect, so we can
923 }1019 // ignore the OOM here.
1020 self.text_block_free_list.append(self.allocator, prev) catch {};
1021 }
1022 } else {
1023 text_block.prev = null;
1024 }
9241025
925 // This function can also reallocate a text block.1026 if (text_block.next) |next| {
926 // In this case we need to "unplug" it from its previous location before1027 next.prev = text_block.prev;
927 // plugging it in to its new location.1028 } else {
928 if (text_block.prev) |prev| {1029 text_block.next = null;
929 prev.next = text_block.next;1030 }
930 }
931 if (text_block.next) |next| {
932 next.prev = text_block.prev;
933 }1031 }
9341032
935 if (block_placement) |big_block| {1033 fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
936 text_block.prev = big_block;1034 // TODO check the new capacity, and if it crosses the size threshold into a big enough
937 text_block.next = big_block.next;1035 // capacity, insert a free list node for it.
938 big_block.next = text_block;
939 } else {
940 text_block.prev = null;
941 text_block.next = null;
942 }1036 }
943 if (free_list_removal) |i| {
944 _ = self.text_block_free_list.swapRemove(i);
945 }
946 return vaddr;
947 }
9481037
949 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {1038 fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
950 if (decl.link.local_sym_index != 0) return;1039 const sym = self.local_symbols.items[text_block.local_sym_index];
9511040 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
952 // Here we also ensure capacity for the free lists so that they can be appended to without fail.1041 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
953 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);1042 if (!need_realloc) return sym.st_value;
954 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);1043 return self.allocateTextBlock(text_block, new_block_size, alignment);
955 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
956 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
957
958 if (self.local_symbol_free_list.popOrNull()) |i| {
959 //std.debug.warn("reusing symbol index {} for {}\n", .{i, decl.name});
960 decl.link.local_sym_index = i;
961 } else {
962 //std.debug.warn("allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
963 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
964 _ = self.local_symbols.addOneAssumeCapacity();
965 }1044 }
9661045
967 if (self.offset_table_free_list.popOrNull()) |i| {1046 fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
968 decl.link.offset_table_index = i;1047 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
969 } else {1048 const shdr = &self.sections.items[self.text_section_index.?];
970 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);1049 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
971 _ = self.offset_table.addOneAssumeCapacity();1050
972 self.offset_table_count_dirty = true;1051 // We use these to indicate our intention to update metadata, placing the new block,
973 }1052 // and possibly removing a free list node.
1053 // It would be simpler to do it inside the for loop below, but that would cause a
1054 // problem if an error was returned later in the function. So this action
1055 // is actually carried out at the end of the function, when errors are no longer possible.
1056 var block_placement: ?*TextBlock = null;
1057 var free_list_removal: ?usize = null;
1058
1059 // First we look for an appropriately sized free list node.
1060 // The list is unordered. We'll just take the first thing that works.
1061 const vaddr = blk: {
1062 var i: usize = 0;
1063 while (i < self.text_block_free_list.items.len) {
1064 const big_block = self.text_block_free_list.items[i];
1065 // We now have a pointer to a live text block that has too much capacity.
1066 // Is it enough that we could fit this new text block?
1067 const sym = self.local_symbols.items[big_block.local_sym_index];
1068 const capacity = big_block.capacity(self.*);
1069 const ideal_capacity = capacity * alloc_num / alloc_den;
1070 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1071 const capacity_end_vaddr = sym.st_value + capacity;
1072 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
1073 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
1074 if (new_start_vaddr < ideal_capacity_end_vaddr) {
1075 // Additional bookkeeping here to notice if this free list node
1076 // should be deleted because the block that it points to has grown to take up
1077 // more of the extra capacity.
1078 if (!big_block.freeListEligible(self.*)) {
1079 _ = self.text_block_free_list.swapRemove(i);
1080 } else {
1081 i += 1;
1082 }
1083 continue;
1084 }
1085 // At this point we know that we will place the new block here. But the
1086 // remaining question is whether there is still yet enough capacity left
1087 // over for there to still be a free list node.
1088 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
1089 const keep_free_list_node = remaining_capacity >= min_text_capacity;
1090
1091 // Set up the metadata to be updated, after errors are no longer possible.
1092 block_placement = big_block;
1093 if (!keep_free_list_node) {
1094 free_list_removal = i;
1095 }
1096 break :blk new_start_vaddr;
1097 } else if (self.last_text_block) |last| {
1098 const sym = self.local_symbols.items[last.local_sym_index];
1099 const ideal_capacity = sym.st_size * alloc_num / alloc_den;
1100 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1101 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
1102 // Set up the metadata to be updated, after errors are no longer possible.
1103 block_placement = last;
1104 break :blk new_start_vaddr;
1105 } else {
1106 break :blk phdr.p_vaddr;
1107 }
1108 };
9741109
975 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];1110 const expand_text_section = block_placement == null or block_placement.?.next == null;
1111 if (expand_text_section) {
1112 const text_capacity = self.allocatedSize(shdr.sh_offset);
1113 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
1114 if (needed_size > text_capacity) {
1115 // Must move the entire text section.
1116 const new_offset = self.findFreeSpace(needed_size, 0x1000);
1117 const text_size = if (self.last_text_block) |last| blk: {
1118 const sym = self.local_symbols.items[last.local_sym_index];
1119 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
1120 } else 0;
1121 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
1122 if (amt != text_size) return error.InputOutput;
1123 shdr.sh_offset = new_offset;
1124 phdr.p_offset = new_offset;
1125 }
1126 self.last_text_block = text_block;
9761127
977 self.local_symbols.items[decl.link.local_sym_index] = .{1128 shdr.sh_size = needed_size;
978 .st_name = 0,1129 phdr.p_memsz = needed_size;
979 .st_info = 0,1130 phdr.p_filesz = needed_size;
980 .st_other = 0,
981 .st_shndx = 0,
982 .st_value = phdr.p_vaddr,
983 .st_size = 0,
984 };
985 self.offset_table.items[decl.link.offset_table_index] = 0;
986 }
9871131
988 pub fn freeDecl(self: *ElfFile, decl: *Module.Decl) void {1132 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
989 self.freeTextBlock(&decl.link);1133 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
990 if (decl.link.local_sym_index != 0) {1134 }
991 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
992 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
9931135
994 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;1136 // This function can also reallocate a text block.
1137 // In this case we need to "unplug" it from its previous location before
1138 // plugging it in to its new location.
1139 if (text_block.prev) |prev| {
1140 prev.next = text_block.next;
1141 }
1142 if (text_block.next) |next| {
1143 next.prev = text_block.prev;
1144 }
9951145
996 decl.link.local_sym_index = 0;1146 if (block_placement) |big_block| {
1147 text_block.prev = big_block;
1148 text_block.next = big_block.next;
1149 big_block.next = text_block;
1150 } else {
1151 text_block.prev = null;
1152 text_block.next = null;
1153 }
1154 if (free_list_removal) |i| {
1155 _ = self.text_block_free_list.swapRemove(i);
1156 }
1157 return vaddr;
997 }1158 }
998 }
9991159
1000 pub fn updateDecl(self: *ElfFile, module: *Module, decl: *Module.Decl) !void {1160 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
1001 var code_buffer = std.ArrayList(u8).init(self.allocator);1161 if (decl.link.local_sym_index != 0) return;
1002 defer code_buffer.deinit();
1003
1004 const typed_value = decl.typed_value.most_recent.typed_value;
1005 const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) {
1006 .externally_managed => |x| x,
1007 .appended => code_buffer.items,
1008 .fail => |em| {
1009 decl.analysis = .codegen_failure;
1010 _ = try module.failed_decls.put(decl, em);
1011 return;
1012 },
1013 };
10141162
1015 const required_alignment = typed_value.ty.abiAlignment(self.options.target);1163 // Here we also ensure capacity for the free lists so that they can be appended to without fail.
1164 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
1165 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
1166 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
1167 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
10161168
1017 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {1169 if (self.local_symbol_free_list.popOrNull()) |i| {
1018 .Fn => elf.STT_FUNC,1170 std.log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });
1019 else => elf.STT_OBJECT,1171 decl.link.local_sym_index = i;
1020 };1172 } else {
1173 std.log.debug(.link, "allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
1174 decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
1175 _ = self.local_symbols.addOneAssumeCapacity();
1176 }
10211177
1022 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()1178 if (self.offset_table_free_list.popOrNull()) |i| {
1023 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];1179 decl.link.offset_table_index = i;
1024 if (local_sym.st_size != 0) {1180 } else {
1025 const capacity = decl.link.capacity(self.*);1181 decl.link.offset_table_index = @intCast(u32, self.offset_table.items.len);
1026 const need_realloc = code.len > capacity or1182 _ = self.offset_table.addOneAssumeCapacity();
1027 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);1183 self.offset_table_count_dirty = true;
1028 if (need_realloc) {
1029 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1030 //std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1031 if (vaddr != local_sym.st_value) {
1032 local_sym.st_value = vaddr;
1033
1034 //std.debug.warn(" (writing new offset table entry)\n", .{});
1035 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1036 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1037 }
1038 } else if (code.len < local_sym.st_size) {
1039 self.shrinkTextBlock(&decl.link, code.len);
1040 }1184 }
1041 local_sym.st_size = code.len;1185
1042 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));1186 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1043 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;1187
1044 local_sym.st_other = 0;1188 self.local_symbols.items[decl.link.local_sym_index] = .{
1045 local_sym.st_shndx = self.text_section_index.?;1189 .st_name = 0,
1046 // TODO this write could be avoided if no fields of the symbol were changed.1190 .st_info = 0,
1047 try self.writeSymbol(decl.link.local_sym_index);
1048 } else {
1049 const decl_name = mem.spanZ(decl.name);
1050 const name_str_index = try self.makeString(decl_name);
1051 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1052 //std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1053 errdefer self.freeTextBlock(&decl.link);
1054
1055 local_sym.* = .{
1056 .st_name = name_str_index,
1057 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
1058 .st_other = 0,1191 .st_other = 0,
1059 .st_shndx = self.text_section_index.?,1192 .st_shndx = 0,
1060 .st_value = vaddr,1193 .st_value = phdr.p_vaddr,
1061 .st_size = code.len,1194 .st_size = 0,
1062 };1195 };
1063 self.offset_table.items[decl.link.offset_table_index] = vaddr;1196 self.offset_table.items[decl.link.offset_table_index] = 0;
1064
1065 try self.writeSymbol(decl.link.local_sym_index);
1066 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1067 }1197 }
10681198
1069 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;1199 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
1070 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;1200 self.freeTextBlock(&decl.link);
1071 try self.file.?.pwriteAll(code, file_offset);1201 if (decl.link.local_sym_index != 0) {
1202 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
1203 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
10721204
1073 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.1205 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
1074 const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*Module.Export{};
1075 return self.updateDeclExports(module, decl, decl_exports);
1076 }
10771206
1078 /// Must be called only after a successful call to `updateDecl`.1207 decl.link.local_sym_index = 0;
1079 pub fn updateDeclExports(
1080 self: *ElfFile,
1081 module: *Module,
1082 decl: *const Module.Decl,
1083 exports: []const *Module.Export,
1084 ) !void {
1085 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1086 // them, so that deleting exports is guaranteed to succeed.
1087 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1088 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1089 const typed_value = decl.typed_value.most_recent.typed_value;
1090 if (decl.link.local_sym_index == 0) return;
1091 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
1092
1093 for (exports) |exp| {
1094 if (exp.options.section) |section_name| {
1095 if (!mem.eql(u8, section_name, ".text")) {
1096 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);
1097 module.failed_exports.putAssumeCapacityNoClobber(
1098 exp,
1099 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
1100 );
1101 continue;
1102 }
1103 }1208 }
1104 const stb_bits: u8 = switch (exp.options.linkage) {1209 }
1105 .Internal => elf.STB_LOCAL,1210
1106 .Strong => blk: {1211 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1107 if (mem.eql(u8, exp.options.name, "_start")) {1212 var code_buffer = std.ArrayList(u8).init(self.allocator);
1108 self.entry_addr = decl_sym.st_value;1213 defer code_buffer.deinit();
1109 }1214
1110 break :blk elf.STB_GLOBAL;1215 const typed_value = decl.typed_value.most_recent.typed_value;
1111 },1216 const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
1112 .Weak => elf.STB_WEAK,1217 .externally_managed => |x| x,
1113 .LinkOnce => {1218 .appended => code_buffer.items,
1114 try module.failed_exports.ensureCapacity(module.failed_exports.size + 1);1219 .fail => |em| {
1115 module.failed_exports.putAssumeCapacityNoClobber(1220 decl.analysis = .codegen_failure;
1116 exp,1221 try module.failed_decls.put(module.gpa, decl, em);
1117 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),1222 return;
1118 );
1119 continue;
1120 },1223 },
1121 };1224 };
1122 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);1225
1123 if (exp.link.sym_index) |i| {1226 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
1124 const sym = &self.global_symbols.items[i];1227
1125 sym.* = .{1228 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1126 .st_name = try self.updateString(sym.st_name, exp.options.name),1229 .Fn => elf.STT_FUNC,
1127 .st_info = (stb_bits << 4) | stt_bits,1230 else => elf.STT_OBJECT,
1128 .st_other = 0,1231 };
1129 .st_shndx = self.text_section_index.?,1232
1130 .st_value = decl_sym.st_value,1233 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1131 .st_size = decl_sym.st_size,1234 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
1132 };1235 if (local_sym.st_size != 0) {
1236 const capacity = decl.link.capacity(self.*);
1237 const need_realloc = code.len > capacity or
1238 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
1239 if (need_realloc) {
1240 const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
1241 std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
1242 if (vaddr != local_sym.st_value) {
1243 local_sym.st_value = vaddr;
1244
1245 std.log.debug(.link, " (writing new offset table entry)\n", .{});
1246 self.offset_table.items[decl.link.offset_table_index] = vaddr;
1247 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1248 }
1249 } else if (code.len < local_sym.st_size) {
1250 self.shrinkTextBlock(&decl.link, code.len);
1251 }
1252 local_sym.st_size = code.len;
1253 local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name));
1254 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
1255 local_sym.st_other = 0;
1256 local_sym.st_shndx = self.text_section_index.?;
1257 // TODO this write could be avoided if no fields of the symbol were changed.
1258 try self.writeSymbol(decl.link.local_sym_index);
1133 } else {1259 } else {
1134 const name = try self.makeString(exp.options.name);1260 const decl_name = mem.spanZ(decl.name);
1135 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {1261 const name_str_index = try self.makeString(decl_name);
1136 _ = self.global_symbols.addOneAssumeCapacity();1262 const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
1137 break :blk self.global_symbols.items.len - 1;1263 std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
1138 };1264 errdefer self.freeTextBlock(&decl.link);
1139 self.global_symbols.items[i] = .{1265
1140 .st_name = name,1266 local_sym.* = .{
1141 .st_info = (stb_bits << 4) | stt_bits,1267 .st_name = name_str_index,
1268 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
1142 .st_other = 0,1269 .st_other = 0,
1143 .st_shndx = self.text_section_index.?,1270 .st_shndx = self.text_section_index.?,
1144 .st_value = decl_sym.st_value,1271 .st_value = vaddr,
1145 .st_size = decl_sym.st_size,1272 .st_size = code.len,
1146 };1273 };
1274 self.offset_table.items[decl.link.offset_table_index] = vaddr;
11471275
1148 exp.link.sym_index = @intCast(u32, i);1276 try self.writeSymbol(decl.link.local_sym_index);
1277 try self.writeOffsetTableEntry(decl.link.offset_table_index);
1149 }1278 }
1150 }
1151 }
11521279
1153 pub fn deleteExport(self: *ElfFile, exp: Export) void {1280 const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
1154 const sym_index = exp.sym_index orelse return;1281 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1155 self.global_symbol_free_list.appendAssumeCapacity(sym_index);1282 try self.file.?.pwriteAll(code, file_offset);
1156 self.global_symbols.items[sym_index].st_info = 0;
1157 }
11581283
1159 fn writeProgHeader(self: *ElfFile, index: usize) !void {1284 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
1160 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1285 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1161 const offset = self.program_headers.items[index].p_offset;1286 return self.updateDeclExports(module, decl, decl_exports);
1162 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1163 32 => {
1164 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1165 if (foreign_endian) {
1166 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
1167 }
1168 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1169 },
1170 64 => {
1171 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
1172 if (foreign_endian) {
1173 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
1174 }
1175 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1176 },
1177 else => return error.UnsupportedArchitecture,
1178 }1287 }
1179 }
11801288
1181 fn writeSectHeader(self: *ElfFile, index: usize) !void {1289 /// Must be called only after a successful call to `updateDecl`.
1182 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1290 pub fn updateDeclExports(
1183 const offset = self.sections.items[index].sh_offset;1291 self: *Elf,
1184 switch (self.options.target.cpu.arch.ptrBitWidth()) {1292 module: *Module,
1185 32 => {1293 decl: *const Module.Decl,
1186 var shdr: [1]elf.Elf32_Shdr = undefined;1294 exports: []const *Module.Export,
1187 shdr[0] = sectHeaderTo32(self.sections.items[index]);1295 ) !void {
1188 if (foreign_endian) {1296 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1189 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);1297 // them, so that deleting exports is guaranteed to succeed.
1190 }1298 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1191 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);1299 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1192 },1300 const typed_value = decl.typed_value.most_recent.typed_value;
1193 64 => {1301 if (decl.link.local_sym_index == 0) return;
1194 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};1302 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
1195 if (foreign_endian) {1303
1196 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);1304 for (exports) |exp| {
1305 if (exp.options.section) |section_name| {
1306 if (!mem.eql(u8, section_name, ".text")) {
1307 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
1308 module.failed_exports.putAssumeCapacityNoClobber(
1309 exp,
1310 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
1311 );
1312 continue;
1313 }
1197 }1314 }
1198 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);1315 const stb_bits: u8 = switch (exp.options.linkage) {
1199 },1316 .Internal => elf.STB_LOCAL,
1200 else => return error.UnsupportedArchitecture,1317 .Strong => blk: {
1201 }1318 if (mem.eql(u8, exp.options.name, "_start")) {
1202 }1319 self.entry_addr = decl_sym.st_value;
1320 }
1321 break :blk elf.STB_GLOBAL;
1322 },
1323 .Weak => elf.STB_WEAK,
1324 .LinkOnce => {
1325 try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
1326 module.failed_exports.putAssumeCapacityNoClobber(
1327 exp,
1328 try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
1329 );
1330 continue;
1331 },
1332 };
1333 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
1334 if (exp.link.sym_index) |i| {
1335 const sym = &self.global_symbols.items[i];
1336 sym.* = .{
1337 .st_name = try self.updateString(sym.st_name, exp.options.name),
1338 .st_info = (stb_bits << 4) | stt_bits,
1339 .st_other = 0,
1340 .st_shndx = self.text_section_index.?,
1341 .st_value = decl_sym.st_value,
1342 .st_size = decl_sym.st_size,
1343 };
1344 } else {
1345 const name = try self.makeString(exp.options.name);
1346 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
1347 _ = self.global_symbols.addOneAssumeCapacity();
1348 break :blk self.global_symbols.items.len - 1;
1349 };
1350 self.global_symbols.items[i] = .{
1351 .st_name = name,
1352 .st_info = (stb_bits << 4) | stt_bits,
1353 .st_other = 0,
1354 .st_shndx = self.text_section_index.?,
1355 .st_value = decl_sym.st_value,
1356 .st_size = decl_sym.st_size,
1357 };
12031358
1204 fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {1359 exp.link.sym_index = @intCast(u32, i);
1205 const shdr = &self.sections.items[self.got_section_index.?];1360 }
1206 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1207 const entry_size: u16 = switch (self.ptr_width) {
1208 .p32 => 4,
1209 .p64 => 8,
1210 };
1211 if (self.offset_table_count_dirty) {
1212 // TODO Also detect virtual address collisions.
1213 const allocated_size = self.allocatedSize(shdr.sh_offset);
1214 const needed_size = self.local_symbols.items.len * entry_size;
1215 if (needed_size > allocated_size) {
1216 // Must move the entire got section.
1217 const new_offset = self.findFreeSpace(needed_size, entry_size);
1218 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1219 if (amt != shdr.sh_size) return error.InputOutput;
1220 shdr.sh_offset = new_offset;
1221 phdr.p_offset = new_offset;
1222 }1361 }
1223 shdr.sh_size = needed_size;1362 }
1224 phdr.p_memsz = needed_size;
1225 phdr.p_filesz = needed_size;
12261363
1227 self.shdr_table_dirty = true; // TODO look into making only the one section dirty1364 pub fn deleteExport(self: *Elf, exp: Export) void {
1228 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty1365 const sym_index = exp.sym_index orelse return;
1366 self.global_symbol_free_list.appendAssumeCapacity(sym_index);
1367 self.global_symbols.items[sym_index].st_info = 0;
1368 }
12291369
1230 self.offset_table_count_dirty = false;1370 fn writeProgHeader(self: *Elf, index: usize) !void {
1371 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1372 const offset = self.program_headers.items[index].p_offset;
1373 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1374 32 => {
1375 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1376 if (foreign_endian) {
1377 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
1378 }
1379 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1380 },
1381 64 => {
1382 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
1383 if (foreign_endian) {
1384 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
1385 }
1386 return self.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
1387 },
1388 else => return error.UnsupportedArchitecture,
1389 }
1231 }1390 }
1232 const endian = self.options.target.cpu.arch.endian();1391
1233 const off = shdr.sh_offset + @as(u64, entry_size) * index;1392 fn writeSectHeader(self: *Elf, index: usize) !void {
1234 switch (self.ptr_width) {1393 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1235 .p32 => {1394 const offset = self.sections.items[index].sh_offset;
1236 var buf: [4]u8 = undefined;1395 switch (self.options.target.cpu.arch.ptrBitWidth()) {
1237 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);1396 32 => {
1238 try self.file.?.pwriteAll(&buf, off);1397 var shdr: [1]elf.Elf32_Shdr = undefined;
1239 },1398 shdr[0] = sectHeaderTo32(self.sections.items[index]);
1240 .p64 => {1399 if (foreign_endian) {
1241 var buf: [8]u8 = undefined;1400 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
1242 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);1401 }
1243 try self.file.?.pwriteAll(&buf, off);1402 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1244 },1403 },
1404 64 => {
1405 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
1406 if (foreign_endian) {
1407 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
1408 }
1409 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1410 },
1411 else => return error.UnsupportedArchitecture,
1412 }
1245 }1413 }
1246 }
12471414
1248 fn writeSymbol(self: *ElfFile, index: usize) !void {1415 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
1249 const syms_sect = &self.sections.items[self.symtab_section_index.?];1416 const shdr = &self.sections.items[self.got_section_index.?];
1250 // Make sure we are not pointlessly writing symbol data that will have to get relocated1417 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1251 // due to running out of space.1418 const entry_size: u16 = switch (self.ptr_width) {
1252 if (self.local_symbols.items.len != syms_sect.sh_info) {1419 .p32 => 4,
1253 const sym_size: u64 = switch (self.ptr_width) {1420 .p64 => 8,
1254 .p32 => @sizeOf(elf.Elf32_Sym),
1255 .p64 => @sizeOf(elf.Elf64_Sym),
1256 };
1257 const sym_align: u16 = switch (self.ptr_width) {
1258 .p32 => @alignOf(elf.Elf32_Sym),
1259 .p64 => @alignOf(elf.Elf64_Sym),
1260 };1421 };
1261 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;1422 if (self.offset_table_count_dirty) {
1262 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {1423 // TODO Also detect virtual address collisions.
1263 // Move all the symbols to a new file location.1424 const allocated_size = self.allocatedSize(shdr.sh_offset);
1264 const new_offset = self.findFreeSpace(needed_size, sym_align);1425 const needed_size = self.local_symbols.items.len * entry_size;
1265 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;1426 if (needed_size > allocated_size) {
1266 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);1427 // Must move the entire got section.
1267 if (amt != existing_size) return error.InputOutput;1428 const new_offset = self.findFreeSpace(needed_size, entry_size);
1268 syms_sect.sh_offset = new_offset;1429 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1430 if (amt != shdr.sh_size) return error.InputOutput;
1431 shdr.sh_offset = new_offset;
1432 phdr.p_offset = new_offset;
1433 }
1434 shdr.sh_size = needed_size;
1435 phdr.p_memsz = needed_size;
1436 phdr.p_filesz = needed_size;
1437
1438 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1439 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1440
1441 self.offset_table_count_dirty = false;
1442 }
1443 const endian = self.options.target.cpu.arch.endian();
1444 const off = shdr.sh_offset + @as(u64, entry_size) * index;
1445 switch (self.ptr_width) {
1446 .p32 => {
1447 var buf: [4]u8 = undefined;
1448 mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
1449 try self.file.?.pwriteAll(&buf, off);
1450 },
1451 .p64 => {
1452 var buf: [8]u8 = undefined;
1453 mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
1454 try self.file.?.pwriteAll(&buf, off);
1455 },
1269 }1456 }
1270 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1271 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1272 self.shdr_table_dirty = true; // TODO look into only writing one section
1273 }1457 }
1274 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1458
1275 switch (self.ptr_width) {1459 fn writeSymbol(self: *Elf, index: usize) !void {
1276 .p32 => {1460 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1277 var sym = [1]elf.Elf32_Sym{1461 // Make sure we are not pointlessly writing symbol data that will have to get relocated
1278 .{1462 // due to running out of space.
1279 .st_name = self.local_symbols.items[index].st_name,1463 if (self.local_symbols.items.len != syms_sect.sh_info) {
1280 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),1464 const sym_size: u64 = switch (self.ptr_width) {
1281 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),1465 .p32 => @sizeOf(elf.Elf32_Sym),
1282 .st_info = self.local_symbols.items[index].st_info,1466 .p64 => @sizeOf(elf.Elf64_Sym),
1283 .st_other = self.local_symbols.items[index].st_other,
1284 .st_shndx = self.local_symbols.items[index].st_shndx,
1285 },
1286 };1467 };
1287 if (foreign_endian) {1468 const sym_align: u16 = switch (self.ptr_width) {
1288 bswapAllFields(elf.Elf32_Sym, &sym[0]);1469 .p32 => @alignOf(elf.Elf32_Sym),
1289 }1470 .p64 => @alignOf(elf.Elf64_Sym),
1290 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;1471 };
1291 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);1472 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
1292 },1473 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
1293 .p64 => {1474 // Move all the symbols to a new file location.
1294 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};1475 const new_offset = self.findFreeSpace(needed_size, sym_align);
1295 if (foreign_endian) {1476 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
1296 bswapAllFields(elf.Elf64_Sym, &sym[0]);1477 const amt = try self.file.?.copyRangeAll(syms_sect.sh_offset, self.file.?, new_offset, existing_size);
1478 if (amt != existing_size) return error.InputOutput;
1479 syms_sect.sh_offset = new_offset;
1297 }1480 }
1298 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;1481 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1299 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);1482 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1300 },1483 self.shdr_table_dirty = true; // TODO look into only writing one section
1301 }1484 }
1302 }1485 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
13031486 switch (self.ptr_width) {
1304 fn writeAllGlobalSymbols(self: *ElfFile) !void {1487 .p32 => {
1305 const syms_sect = &self.sections.items[self.symtab_section_index.?];1488 var sym = [1]elf.Elf32_Sym{
1306 const sym_size: u64 = switch (self.ptr_width) {1489 .{
1307 .p32 => @sizeOf(elf.Elf32_Sym),1490 .st_name = self.local_symbols.items[index].st_name,
1308 .p64 => @sizeOf(elf.Elf64_Sym),1491 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
1309 };1492 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
1310 //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });1493 .st_info = self.local_symbols.items[index].st_info,
1311 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1494 .st_other = self.local_symbols.items[index].st_other,
1312 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;1495 .st_shndx = self.local_symbols.items[index].st_shndx,
1313 switch (self.ptr_width) {1496 },
1314 .p32 => {
1315 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1316 defer self.allocator.free(buf);
1317
1318 for (buf) |*sym, i| {
1319 sym.* = .{
1320 .st_name = self.global_symbols.items[i].st_name,
1321 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1322 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1323 .st_info = self.global_symbols.items[i].st_info,
1324 .st_other = self.global_symbols.items[i].st_other,
1325 .st_shndx = self.global_symbols.items[i].st_shndx,
1326 };1497 };
1327 if (foreign_endian) {1498 if (foreign_endian) {
1328 bswapAllFields(elf.Elf32_Sym, sym);1499 bswapAllFields(elf.Elf32_Sym, &sym[0]);
1329 }1500 }
1330 }1501 const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
1331 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);1502 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1332 },1503 },
1333 .p64 => {1504 .p64 => {
1334 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);1505 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
1335 defer self.allocator.free(buf);
1336
1337 for (buf) |*sym, i| {
1338 sym.* = .{
1339 .st_name = self.global_symbols.items[i].st_name,
1340 .st_value = self.global_symbols.items[i].st_value,
1341 .st_size = self.global_symbols.items[i].st_size,
1342 .st_info = self.global_symbols.items[i].st_info,
1343 .st_other = self.global_symbols.items[i].st_other,
1344 .st_shndx = self.global_symbols.items[i].st_shndx,
1345 };
1346 if (foreign_endian) {1506 if (foreign_endian) {
1347 bswapAllFields(elf.Elf64_Sym, sym);1507 bswapAllFields(elf.Elf64_Sym, &sym[0]);
1348 }1508 }
1349 }1509 const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
1350 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);1510 try self.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1351 },1511 },
1512 }
1352 }1513 }
1353 }1514
1515 fn writeAllGlobalSymbols(self: *Elf) !void {
1516 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1517 const sym_size: u64 = switch (self.ptr_width) {
1518 .p32 => @sizeOf(elf.Elf32_Sym),
1519 .p64 => @sizeOf(elf.Elf64_Sym),
1520 };
1521 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1522 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
1523 switch (self.ptr_width) {
1524 .p32 => {
1525 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1526 defer self.allocator.free(buf);
1527
1528 for (buf) |*sym, i| {
1529 sym.* = .{
1530 .st_name = self.global_symbols.items[i].st_name,
1531 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1532 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1533 .st_info = self.global_symbols.items[i].st_info,
1534 .st_other = self.global_symbols.items[i].st_other,
1535 .st_shndx = self.global_symbols.items[i].st_shndx,
1536 };
1537 if (foreign_endian) {
1538 bswapAllFields(elf.Elf32_Sym, sym);
1539 }
1540 }
1541 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1542 },
1543 .p64 => {
1544 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
1545 defer self.allocator.free(buf);
1546
1547 for (buf) |*sym, i| {
1548 sym.* = .{
1549 .st_name = self.global_symbols.items[i].st_name,
1550 .st_value = self.global_symbols.items[i].st_value,
1551 .st_size = self.global_symbols.items[i].st_size,
1552 .st_info = self.global_symbols.items[i].st_info,
1553 .st_other = self.global_symbols.items[i].st_other,
1554 .st_shndx = self.global_symbols.items[i].st_shndx,
1555 };
1556 if (foreign_endian) {
1557 bswapAllFields(elf.Elf64_Sym, sym);
1558 }
1559 }
1560 try self.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1561 },
1562 }
1563 }
1564 };
1354};1565};
13551566
1356/// Truncates the existing file contents and overwrites the contents.1567/// Truncates the existing file contents and overwrites the contents.
1357/// Returns an error if `file` is not already open with +read +write +seek abilities.1568/// Returns an error if `file` is not already open with +read +write +seek abilities.
1358pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {1569pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
1359 switch (options.output_mode) {1570 switch (options.output_mode) {
1360 .Exe => {},1571 .Exe => {},
1361 .Obj => {},1572 .Obj => {},
1362 .Lib => return error.TODOImplementWritingLibFiles,1573 .Lib => return error.TODOImplementWritingLibFiles,
1363 }1574 }
1364 switch (options.object_format) {1575 switch (options.object_format) {
1576 .c => unreachable,
1365 .unknown => unreachable, // TODO remove this tag from the enum1577 .unknown => unreachable, // TODO remove this tag from the enum
1366 .coff => return error.TODOImplementWritingCOFF,1578 .coff => return error.TODOImplementWritingCOFF,
1367 .elf => {},1579 .elf => {},
...@@ -1369,7 +1581,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El...@@ -1369,7 +1581,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
1369 .wasm => return error.TODOImplementWritingWasmObjects,1581 .wasm => return error.TODOImplementWritingWasmObjects,
1370 }1582 }
13711583
1372 var self: ElfFile = .{1584 var self: File.Elf = .{
1373 .allocator = allocator,1585 .allocator = allocator,
1374 .file = file,1586 .file = file,
1375 .options = options,1587 .options = options,
...@@ -1413,7 +1625,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El...@@ -1413,7 +1625,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
1413}1625}
14141626
1415/// Returns error.IncrFailed if incremental update could not be performed.1627/// Returns error.IncrFailed if incremental update could not be performed.
1416fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !ElfFile {1628fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !File.Elf {
1417 switch (options.output_mode) {1629 switch (options.output_mode) {
1418 .Exe => {},1630 .Exe => {},
1419 .Obj => {},1631 .Obj => {},
...@@ -1421,12 +1633,13 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf...@@ -1421,12 +1633,13 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf
1421 }1633 }
1422 switch (options.object_format) {1634 switch (options.object_format) {
1423 .unknown => unreachable, // TODO remove this tag from the enum1635 .unknown => unreachable, // TODO remove this tag from the enum
1636 .c => unreachable,
1424 .coff => return error.IncrFailed,1637 .coff => return error.IncrFailed,
1425 .elf => {},1638 .elf => {},
1426 .macho => return error.IncrFailed,1639 .macho => return error.IncrFailed,
1427 .wasm => return error.IncrFailed,1640 .wasm => return error.IncrFailed,
1428 }1641 }
1429 var self: ElfFile = .{1642 var self: File.Elf = .{
1430 .allocator = allocator,1643 .allocator = allocator,
1431 .file = file,1644 .file = file,
1432 .owns_file_handle = false,1645 .owns_file_handle = false,
...@@ -1446,7 +1659,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf...@@ -1446,7 +1659,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf
1446}1659}
14471660
1448/// Saturating multiplication1661/// Saturating multiplication
1449fn satMul(a: var, b: var) @TypeOf(a, b) {1662fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
1450 const T = @TypeOf(a, b);1663 const T = @TypeOf(a, b);
1451 return std.math.mul(T, a, b) catch std.math.maxInt(T);1664 return std.math.mul(T, a, b) catch std.math.maxInt(T);
1452}1665}
src-self-hosted/liveness.zig created+158
...@@ -0,0 +1,158 @@
1const std = @import("std");
2const ir = @import("ir.zig");
3const trace = @import("tracy.zig").trace;
4
5/// Perform Liveness Analysis over the `Body`. Each `Inst` will have its `deaths` field populated.
6pub fn analyze(
7 /// Used for temporary storage during the analysis.
8 gpa: *std.mem.Allocator,
9 /// Used to tack on extra allocations in the same lifetime as the existing instructions.
10 arena: *std.mem.Allocator,
11 body: ir.Body,
12) error{OutOfMemory}!void {
13 const tracy = trace(@src());
14 defer tracy.end();
15
16 var table = std.AutoHashMap(*ir.Inst, void).init(gpa);
17 defer table.deinit();
18 try table.ensureCapacity(body.instructions.len);
19 try analyzeWithTable(arena, &table, body);
20}
21
22fn analyzeWithTable(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), body: ir.Body) error{OutOfMemory}!void {
23 var i: usize = body.instructions.len;
24
25 while (i != 0) {
26 i -= 1;
27 const base = body.instructions[i];
28 try analyzeInstGeneric(arena, table, base);
29 }
30}
31
32fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), base: *ir.Inst) error{OutOfMemory}!void {
33 // Obtain the corresponding instruction type based on the tag type.
34 inline for (std.meta.declarations(ir.Inst)) |decl| {
35 switch (decl.data) {
36 .Type => |T| {
37 if (@typeInfo(T) == .Struct and @hasDecl(T, "base_tag")) {
38 if (T.base_tag == base.tag) {
39 return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base));
40 }
41 }
42 },
43 else => {},
44 }
45 }
46 unreachable;
47}
48
49fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), comptime T: type, inst: *T) error{OutOfMemory}!void {
50 if (table.contains(&inst.base)) {
51 inst.base.deaths = 0;
52 } else {
53 // No tombstone for this instruction means it is never referenced,
54 // and its birth marks its own death. Very metal 🤘
55 inst.base.deaths = 1 << ir.Inst.unreferenced_bit_index;
56 }
57
58 switch (T) {
59 ir.Inst.Constant => return,
60 ir.Inst.Block => {
61 try analyzeWithTable(arena, table, inst.args.body);
62 // We let this continue so that it can possibly mark the block as
63 // unreferenced below.
64 },
65 ir.Inst.CondBr => {
66 var true_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
67 defer true_table.deinit();
68 try true_table.ensureCapacity(inst.args.true_body.instructions.len);
69 try analyzeWithTable(arena, &true_table, inst.args.true_body);
70
71 var false_table = std.AutoHashMap(*ir.Inst, void).init(table.allocator);
72 defer false_table.deinit();
73 try false_table.ensureCapacity(inst.args.false_body.instructions.len);
74 try analyzeWithTable(arena, &false_table, inst.args.false_body);
75
76 // Each death that occurs inside one branch, but not the other, needs
77 // to be added as a death immediately upon entering the other branch.
78 // During the iteration of the table, we additionally propagate the
79 // deaths to the parent table.
80 var true_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
81 defer true_entry_deaths.deinit();
82 var false_entry_deaths = std.ArrayList(*ir.Inst).init(table.allocator);
83 defer false_entry_deaths.deinit();
84 {
85 var it = false_table.iterator();
86 while (it.next()) |entry| {
87 const false_death = entry.key;
88 if (!true_table.contains(false_death)) {
89 try true_entry_deaths.append(false_death);
90 // Here we are only adding to the parent table if the following iteration
91 // would miss it.
92 try table.putNoClobber(false_death, {});
93 }
94 }
95 }
96 {
97 var it = true_table.iterator();
98 while (it.next()) |entry| {
99 const true_death = entry.key;
100 try table.putNoClobber(true_death, {});
101 if (!false_table.contains(true_death)) {
102 try false_entry_deaths.append(true_death);
103 }
104 }
105 }
106 inst.true_death_count = std.math.cast(@TypeOf(inst.true_death_count), true_entry_deaths.items.len) catch return error.OutOfMemory;
107 inst.false_death_count = std.math.cast(@TypeOf(inst.false_death_count), false_entry_deaths.items.len) catch return error.OutOfMemory;
108 const allocated_slice = try arena.alloc(*ir.Inst, true_entry_deaths.items.len + false_entry_deaths.items.len);
109 inst.deaths = allocated_slice.ptr;
110
111 // Continue on with the instruction analysis. The following code will find the condition
112 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
113 // condition's lifetime ends immediately before entering any branch.
114 },
115 ir.Inst.Call => {
116 // Call instructions have a runtime-known number of operands so we have to handle them ourselves here.
117 const needed_bits = 1 + inst.args.args.len;
118 if (needed_bits <= ir.Inst.deaths_bits) {
119 var bit_i: ir.Inst.DeathsBitIndex = 0;
120 {
121 const prev = try table.fetchPut(inst.args.func, {});
122 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
123 bit_i += 1;
124 }
125 for (inst.args.args) |arg| {
126 const prev = try table.fetchPut(arg, {});
127 if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
128 bit_i += 1;
129 }
130 } else {
131 @panic("Handle liveness analysis for function calls with many parameters");
132 }
133 },
134 else => {},
135 }
136
137 const Args = ir.Inst.Args(T);
138 if (Args == void) {
139 return;
140 }
141
142 comptime var arg_index: usize = 0;
143 inline for (std.meta.fields(Args)) |field| {
144 if (field.field_type == *ir.Inst) {
145 if (arg_index >= 6) {
146 @compileError("out of bits to mark deaths of operands");
147 }
148 const prev = try table.fetchPut(@field(inst.args, field.name), {});
149 if (prev == null) {
150 // Death.
151 inst.base.deaths |= 1 << arg_index;
152 }
153 arg_index += 1;
154 }
155 }
156
157 std.log.debug(.liveness, "analyze {}: 0b{b}\n", .{ inst.base.tag, inst.base.deaths });
158}
src-self-hosted/main.zig+176-99
...@@ -38,6 +38,32 @@ const usage =...@@ -38,6 +38,32 @@ const usage =
38 \\38 \\
39;39;
4040
41pub fn log(
42 comptime level: std.log.Level,
43 comptime scope: @TypeOf(.EnumLiteral),
44 comptime format: []const u8,
45 args: anytype,
46) void {
47 if (@enumToInt(level) > @enumToInt(std.log.level))
48 return;
49
50 const scope_prefix = "(" ++ switch (scope) {
51 // Uncomment to hide logs
52 //.compiler,
53 .module,
54 .liveness,
55 .link,
56 => return,
57
58 else => @tagName(scope),
59 } ++ "): ";
60
61 const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
62
63 // Print the message to stderr, silently ignoring any errors
64 std.debug.print(prefix ++ format, args);
65}
66
41pub fn main() !void {67pub fn main() !void {
42 // TODO general purpose allocator in the zig std lib68 // TODO general purpose allocator in the zig std lib
43 const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator;69 const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator;
...@@ -48,7 +74,7 @@ pub fn main() !void {...@@ -48,7 +74,7 @@ pub fn main() !void {
48 const args = try process.argsAlloc(arena);74 const args = try process.argsAlloc(arena);
4975
50 if (args.len <= 1) {76 if (args.len <= 1) {
51 std.debug.warn("expected command argument\n\n{}", .{usage});77 std.debug.print("expected command argument\n\n{}", .{usage});
52 process.exit(1);78 process.exit(1);
53 }79 }
5480
...@@ -68,14 +94,14 @@ pub fn main() !void {...@@ -68,14 +94,14 @@ pub fn main() !void {
68 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);94 return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
69 } else if (mem.eql(u8, cmd, "version")) {95 } else if (mem.eql(u8, cmd, "version")) {
70 // Need to set up the build script to give the version as a comptime value.96 // Need to set up the build script to give the version as a comptime value.
71 std.debug.warn("TODO version command not implemented yet\n", .{});97 std.debug.print("TODO version command not implemented yet\n", .{});
72 return error.Unimplemented;98 return error.Unimplemented;
73 } else if (mem.eql(u8, cmd, "zen")) {99 } else if (mem.eql(u8, cmd, "zen")) {
74 try io.getStdOut().writeAll(info_zen);100 try io.getStdOut().writeAll(info_zen);
75 } else if (mem.eql(u8, cmd, "help")) {101 } else if (mem.eql(u8, cmd, "help")) {
76 try io.getStdOut().writeAll(usage);102 try io.getStdOut().writeAll(usage);
77 } else {103 } else {
78 std.debug.warn("unknown command: {}\n\n{}", .{ args[1], usage });104 std.debug.print("unknown command: {}\n\n{}", .{ args[1], usage });
79 process.exit(1);105 process.exit(1);
80 }106 }
81}107}
...@@ -86,7 +112,7 @@ const usage_build_generic =...@@ -86,7 +112,7 @@ const usage_build_generic =
86 \\ zig build-obj <options> [files]112 \\ zig build-obj <options> [files]
87 \\113 \\
88 \\Supported file types:114 \\Supported file types:
89 \\ (planned) .zig Zig source code115 \\ .zig Zig source code
90 \\ .zir Zig Intermediate Representation code116 \\ .zir Zig Intermediate Representation code
91 \\ (planned) .o ELF object file117 \\ (planned) .o ELF object file
92 \\ (planned) .o MACH-O (macOS) object file118 \\ (planned) .o MACH-O (macOS) object file
...@@ -169,6 +195,7 @@ fn buildOutputType(...@@ -169,6 +195,7 @@ fn buildOutputType(
169 var target_arch_os_abi: []const u8 = "native";195 var target_arch_os_abi: []const u8 = "native";
170 var target_mcpu: ?[]const u8 = null;196 var target_mcpu: ?[]const u8 = null;
171 var target_dynamic_linker: ?[]const u8 = null;197 var target_dynamic_linker: ?[]const u8 = null;
198 var object_format: ?std.builtin.ObjectFormat = null;
172199
173 var system_libs = std.ArrayList([]const u8).init(gpa);200 var system_libs = std.ArrayList([]const u8).init(gpa);
174 defer system_libs.deinit();201 defer system_libs.deinit();
...@@ -183,7 +210,7 @@ fn buildOutputType(...@@ -183,7 +210,7 @@ fn buildOutputType(
183 process.exit(0);210 process.exit(0);
184 } else if (mem.eql(u8, arg, "--color")) {211 } else if (mem.eql(u8, arg, "--color")) {
185 if (i + 1 >= args.len) {212 if (i + 1 >= args.len) {
186 std.debug.warn("expected [auto|on|off] after --color\n", .{});213 std.debug.print("expected [auto|on|off] after --color\n", .{});
187 process.exit(1);214 process.exit(1);
188 }215 }
189 i += 1;216 i += 1;
...@@ -195,12 +222,12 @@ fn buildOutputType(...@@ -195,12 +222,12 @@ fn buildOutputType(
195 } else if (mem.eql(u8, next_arg, "off")) {222 } else if (mem.eql(u8, next_arg, "off")) {
196 color = .Off;223 color = .Off;
197 } else {224 } else {
198 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});225 std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
199 process.exit(1);226 process.exit(1);
200 }227 }
201 } else if (mem.eql(u8, arg, "--mode")) {228 } else if (mem.eql(u8, arg, "--mode")) {
202 if (i + 1 >= args.len) {229 if (i + 1 >= args.len) {
203 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});230 std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
204 process.exit(1);231 process.exit(1);
205 }232 }
206 i += 1;233 i += 1;
...@@ -214,52 +241,58 @@ fn buildOutputType(...@@ -214,52 +241,58 @@ fn buildOutputType(
214 } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {241 } else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
215 build_mode = .ReleaseSmall;242 build_mode = .ReleaseSmall;
216 } else {243 } else {
217 std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});244 std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
218 process.exit(1);245 process.exit(1);
219 }246 }
220 } else if (mem.eql(u8, arg, "--name")) {247 } else if (mem.eql(u8, arg, "--name")) {
221 if (i + 1 >= args.len) {248 if (i + 1 >= args.len) {
222 std.debug.warn("expected parameter after --name\n", .{});249 std.debug.print("expected parameter after --name\n", .{});
223 process.exit(1);250 process.exit(1);
224 }251 }
225 i += 1;252 i += 1;
226 provided_name = args[i];253 provided_name = args[i];
227 } else if (mem.eql(u8, arg, "--library")) {254 } else if (mem.eql(u8, arg, "--library")) {
228 if (i + 1 >= args.len) {255 if (i + 1 >= args.len) {
229 std.debug.warn("expected parameter after --library\n", .{});256 std.debug.print("expected parameter after --library\n", .{});
230 process.exit(1);257 process.exit(1);
231 }258 }
232 i += 1;259 i += 1;
233 try system_libs.append(args[i]);260 try system_libs.append(args[i]);
234 } else if (mem.eql(u8, arg, "--version")) {261 } else if (mem.eql(u8, arg, "--version")) {
235 if (i + 1 >= args.len) {262 if (i + 1 >= args.len) {
236 std.debug.warn("expected parameter after --version\n", .{});263 std.debug.print("expected parameter after --version\n", .{});
237 process.exit(1);264 process.exit(1);
238 }265 }
239 i += 1;266 i += 1;
240 version = std.builtin.Version.parse(args[i]) catch |err| {267 version = std.builtin.Version.parse(args[i]) catch |err| {
241 std.debug.warn("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });268 std.debug.print("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
242 process.exit(1);269 process.exit(1);
243 };270 };
244 } else if (mem.eql(u8, arg, "-target")) {271 } else if (mem.eql(u8, arg, "-target")) {
245 if (i + 1 >= args.len) {272 if (i + 1 >= args.len) {
246 std.debug.warn("expected parameter after -target\n", .{});273 std.debug.print("expected parameter after -target\n", .{});
247 process.exit(1);274 process.exit(1);
248 }275 }
249 i += 1;276 i += 1;
250 target_arch_os_abi = args[i];277 target_arch_os_abi = args[i];
251 } else if (mem.eql(u8, arg, "-mcpu")) {278 } else if (mem.eql(u8, arg, "-mcpu")) {
252 if (i + 1 >= args.len) {279 if (i + 1 >= args.len) {
253 std.debug.warn("expected parameter after -mcpu\n", .{});280 std.debug.print("expected parameter after -mcpu\n", .{});
254 process.exit(1);281 process.exit(1);
255 }282 }
256 i += 1;283 i += 1;
257 target_mcpu = args[i];284 target_mcpu = args[i];
285 } else if (mem.eql(u8, arg, "--c")) {
286 if (object_format) |old| {
287 std.debug.print("attempted to override object format {} with C\n", .{old});
288 process.exit(1);
289 }
290 object_format = .c;
258 } else if (mem.startsWith(u8, arg, "-mcpu=")) {291 } else if (mem.startsWith(u8, arg, "-mcpu=")) {
259 target_mcpu = arg["-mcpu=".len..];292 target_mcpu = arg["-mcpu=".len..];
260 } else if (mem.eql(u8, arg, "--dynamic-linker")) {293 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
261 if (i + 1 >= args.len) {294 if (i + 1 >= args.len) {
262 std.debug.warn("expected parameter after --dynamic-linker\n", .{});295 std.debug.print("expected parameter after --dynamic-linker\n", .{});
263 process.exit(1);296 process.exit(1);
264 }297 }
265 i += 1;298 i += 1;
...@@ -301,39 +334,39 @@ fn buildOutputType(...@@ -301,39 +334,39 @@ fn buildOutputType(
301 } else if (mem.startsWith(u8, arg, "-l")) {334 } else if (mem.startsWith(u8, arg, "-l")) {
302 try system_libs.append(arg[2..]);335 try system_libs.append(arg[2..]);
303 } else {336 } else {
304 std.debug.warn("unrecognized parameter: '{}'", .{arg});337 std.debug.print("unrecognized parameter: '{}'", .{arg});
305 process.exit(1);338 process.exit(1);
306 }339 }
307 } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {340 } else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {
308 std.debug.warn("assembly files not supported yet", .{});341 std.debug.print("assembly files not supported yet", .{});
309 process.exit(1);342 process.exit(1);
310 } else if (mem.endsWith(u8, arg, ".o") or343 } else if (mem.endsWith(u8, arg, ".o") or
311 mem.endsWith(u8, arg, ".obj") or344 mem.endsWith(u8, arg, ".obj") or
312 mem.endsWith(u8, arg, ".a") or345 mem.endsWith(u8, arg, ".a") or
313 mem.endsWith(u8, arg, ".lib"))346 mem.endsWith(u8, arg, ".lib"))
314 {347 {
315 std.debug.warn("object files and static libraries not supported yet", .{});348 std.debug.print("object files and static libraries not supported yet", .{});
316 process.exit(1);349 process.exit(1);
317 } else if (mem.endsWith(u8, arg, ".c") or350 } else if (mem.endsWith(u8, arg, ".c") or
318 mem.endsWith(u8, arg, ".cpp"))351 mem.endsWith(u8, arg, ".cpp"))
319 {352 {
320 std.debug.warn("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});353 std.debug.print("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
321 process.exit(1);354 process.exit(1);
322 } else if (mem.endsWith(u8, arg, ".so") or355 } else if (mem.endsWith(u8, arg, ".so") or
323 mem.endsWith(u8, arg, ".dylib") or356 mem.endsWith(u8, arg, ".dylib") or
324 mem.endsWith(u8, arg, ".dll"))357 mem.endsWith(u8, arg, ".dll"))
325 {358 {
326 std.debug.warn("linking against dynamic libraries not yet supported", .{});359 std.debug.print("linking against dynamic libraries not yet supported", .{});
327 process.exit(1);360 process.exit(1);
328 } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {361 } else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
329 if (root_src_file) |other| {362 if (root_src_file) |other| {
330 std.debug.warn("found another zig file '{}' after root source file '{}'", .{ arg, other });363 std.debug.print("found another zig file '{}' after root source file '{}'", .{ arg, other });
331 process.exit(1);364 process.exit(1);
332 } else {365 } else {
333 root_src_file = arg;366 root_src_file = arg;
334 }367 }
335 } else {368 } else {
336 std.debug.warn("unrecognized file extension of parameter '{}'", .{arg});369 std.debug.print("unrecognized file extension of parameter '{}'", .{arg});
337 }370 }
338 }371 }
339 }372 }
...@@ -344,13 +377,13 @@ fn buildOutputType(...@@ -344,13 +377,13 @@ fn buildOutputType(
344 var it = mem.split(basename, ".");377 var it = mem.split(basename, ".");
345 break :blk it.next() orelse basename;378 break :blk it.next() orelse basename;
346 } else {379 } else {
347 std.debug.warn("--name [name] not provided and unable to infer\n", .{});380 std.debug.print("--name [name] not provided and unable to infer\n", .{});
348 process.exit(1);381 process.exit(1);
349 }382 }
350 };383 };
351384
352 if (system_libs.items.len != 0) {385 if (system_libs.items.len != 0) {
353 std.debug.warn("linking against system libraries not yet supported", .{});386 std.debug.print("linking against system libraries not yet supported", .{});
354 process.exit(1);387 process.exit(1);
355 }388 }
356389
...@@ -362,17 +395,17 @@ fn buildOutputType(...@@ -362,17 +395,17 @@ fn buildOutputType(
362 .diagnostics = &diags,395 .diagnostics = &diags,
363 }) catch |err| switch (err) {396 }) catch |err| switch (err) {
364 error.UnknownCpuModel => {397 error.UnknownCpuModel => {
365 std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{398 std.debug.print("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
366 diags.cpu_name.?,399 diags.cpu_name.?,
367 @tagName(diags.arch.?),400 @tagName(diags.arch.?),
368 });401 });
369 for (diags.arch.?.allCpuModels()) |cpu| {402 for (diags.arch.?.allCpuModels()) |cpu| {
370 std.debug.warn(" {}\n", .{cpu.name});403 std.debug.print(" {}\n", .{cpu.name});
371 }404 }
372 process.exit(1);405 process.exit(1);
373 },406 },
374 error.UnknownCpuFeature => {407 error.UnknownCpuFeature => {
375 std.debug.warn(408 std.debug.print(
376 \\Unknown CPU feature: '{}'409 \\Unknown CPU feature: '{}'
377 \\Available CPU features for architecture '{}':410 \\Available CPU features for architecture '{}':
378 \\411 \\
...@@ -381,47 +414,36 @@ fn buildOutputType(...@@ -381,47 +414,36 @@ fn buildOutputType(
381 @tagName(diags.arch.?),414 @tagName(diags.arch.?),
382 });415 });
383 for (diags.arch.?.allFeaturesList()) |feature| {416 for (diags.arch.?.allFeaturesList()) |feature| {
384 std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });417 std.debug.print(" {}: {}\n", .{ feature.name, feature.description });
385 }418 }
386 process.exit(1);419 process.exit(1);
387 },420 },
388 else => |e| return e,421 else => |e| return e,
389 };422 };
390423
391 const object_format: ?std.builtin.ObjectFormat = null;
392 var target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);424 var target_info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
393 if (target_info.cpu_detection_unimplemented) {425 if (target_info.cpu_detection_unimplemented) {
394 // TODO We want to just use detected_info.target but implementing426 // TODO We want to just use detected_info.target but implementing
395 // CPU model & feature detection is todo so here we rely on LLVM.427 // CPU model & feature detection is todo so here we rely on LLVM.
396 std.debug.warn("CPU features detection is not yet available for this system without LLVM extensions\n", .{});428 std.debug.print("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
397 process.exit(1);429 process.exit(1);
398 }430 }
399431
400 const src_path = root_src_file orelse {432 const src_path = root_src_file orelse {
401 std.debug.warn("expected at least one file argument", .{});433 std.debug.print("expected at least one file argument", .{});
402 process.exit(1);434 process.exit(1);
403 };435 };
404436
405 const bin_path = switch (emit_bin) {437 const bin_path = switch (emit_bin) {
406 .no => {438 .no => {
407 std.debug.warn("-fno-emit-bin not supported yet", .{});439 std.debug.print("-fno-emit-bin not supported yet", .{});
408 process.exit(1);440 process.exit(1);
409 },441 },
410 .yes_default_path => switch (output_mode) {442 .yes_default_path => if (object_format != null and object_format.? == .c)
411 .Exe => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }),443 try std.fmt.allocPrint(arena, "{}.c", .{root_name})
412 .Lib => blk: {444 else
413 const suffix = switch (link_mode orelse .Static) {445 try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode),
414 .Static => target_info.target.staticLibSuffix(),446
415 .Dynamic => target_info.target.dynamicLibSuffix(),
416 };
417 break :blk try std.fmt.allocPrint(arena, "{}{}{}", .{
418 target_info.target.libPrefix(),
419 root_name,
420 suffix,
421 });
422 },
423 .Obj => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.oFileExt() }),
424 },
425 .yes => |p| p,447 .yes => |p| p,
426 };448 };
427449
...@@ -450,6 +472,7 @@ fn buildOutputType(...@@ -450,6 +472,7 @@ fn buildOutputType(
450 .link_mode = link_mode,472 .link_mode = link_mode,
451 .object_format = object_format,473 .object_format = object_format,
452 .optimize_mode = build_mode,474 .optimize_mode = build_mode,
475 .keep_source_files_loaded = zir_out_path != null,
453 });476 });
454 defer module.deinit();477 defer module.deinit();
455478
...@@ -487,20 +510,24 @@ fn buildOutputType(...@@ -487,20 +510,24 @@ fn buildOutputType(
487}510}
488511
489fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void {512fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void {
513 var timer = try std.time.Timer.start();
490 try module.update();514 try module.update();
515 const update_nanos = timer.read();
491516
492 var errors = try module.getAllErrorsAlloc();517 var errors = try module.getAllErrorsAlloc();
493 defer errors.deinit(module.allocator);518 defer errors.deinit(module.gpa);
494519
495 if (errors.list.len != 0) {520 if (errors.list.len != 0) {
496 for (errors.list) |full_err_msg| {521 for (errors.list) |full_err_msg| {
497 std.debug.warn("{}:{}:{}: error: {}\n", .{522 std.debug.print("{}:{}:{}: error: {}\n", .{
498 full_err_msg.src_path,523 full_err_msg.src_path,
499 full_err_msg.line + 1,524 full_err_msg.line + 1,
500 full_err_msg.column + 1,525 full_err_msg.column + 1,
501 full_err_msg.msg,526 full_err_msg.msg,
502 });527 });
503 }528 }
529 } else {
530 std.log.info(.compiler, "Update completed in {} ms\n", .{update_nanos / std.time.ns_per_ms});
504 }531 }
505532
506 if (zir_out_path) |zop| {533 if (zir_out_path) |zop| {
...@@ -546,8 +573,9 @@ const Fmt = struct {...@@ -546,8 +573,9 @@ const Fmt = struct {
546 any_error: bool,573 any_error: bool,
547 color: Color,574 color: Color,
548 gpa: *Allocator,575 gpa: *Allocator,
576 out_buffer: std.ArrayList(u8),
549577
550 const SeenMap = std.BufSet;578 const SeenMap = std.AutoHashMap(fs.File.INode, void);
551};579};
552580
553pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {581pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
...@@ -568,7 +596,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -568,7 +596,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
568 process.exit(0);596 process.exit(0);
569 } else if (mem.eql(u8, arg, "--color")) {597 } else if (mem.eql(u8, arg, "--color")) {
570 if (i + 1 >= args.len) {598 if (i + 1 >= args.len) {
571 std.debug.warn("expected [auto|on|off] after --color\n", .{});599 std.debug.print("expected [auto|on|off] after --color\n", .{});
572 process.exit(1);600 process.exit(1);
573 }601 }
574 i += 1;602 i += 1;
...@@ -580,7 +608,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -580,7 +608,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
580 } else if (mem.eql(u8, next_arg, "off")) {608 } else if (mem.eql(u8, next_arg, "off")) {
581 color = .Off;609 color = .Off;
582 } else {610 } else {
583 std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});611 std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
584 process.exit(1);612 process.exit(1);
585 }613 }
586 } else if (mem.eql(u8, arg, "--stdin")) {614 } else if (mem.eql(u8, arg, "--stdin")) {
...@@ -588,7 +616,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -588,7 +616,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
588 } else if (mem.eql(u8, arg, "--check")) {616 } else if (mem.eql(u8, arg, "--check")) {
589 check_flag = true;617 check_flag = true;
590 } else {618 } else {
591 std.debug.warn("unrecognized parameter: '{}'", .{arg});619 std.debug.print("unrecognized parameter: '{}'", .{arg});
592 process.exit(1);620 process.exit(1);
593 }621 }
594 } else {622 } else {
...@@ -599,7 +627,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -599,7 +627,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
599627
600 if (stdin_flag) {628 if (stdin_flag) {
601 if (input_files.items.len != 0) {629 if (input_files.items.len != 0) {
602 std.debug.warn("cannot use --stdin with positional arguments\n", .{});630 std.debug.print("cannot use --stdin with positional arguments\n", .{});
603 process.exit(1);631 process.exit(1);
604 }632 }
605633
...@@ -609,7 +637,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -609,7 +637,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
609 defer gpa.free(source_code);637 defer gpa.free(source_code);
610638
611 const tree = std.zig.parse(gpa, source_code) catch |err| {639 const tree = std.zig.parse(gpa, source_code) catch |err| {
612 std.debug.warn("error parsing stdin: {}\n", .{err});640 std.debug.print("error parsing stdin: {}\n", .{err});
613 process.exit(1);641 process.exit(1);
614 };642 };
615 defer tree.deinit();643 defer tree.deinit();
...@@ -632,7 +660,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -632,7 +660,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
632 }660 }
633661
634 if (input_files.items.len == 0) {662 if (input_files.items.len == 0) {
635 std.debug.warn("expected at least one source file argument\n", .{});663 std.debug.print("expected at least one source file argument\n", .{});
636 process.exit(1);664 process.exit(1);
637 }665 }
638666
...@@ -641,10 +669,20 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {...@@ -641,10 +669,20 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
641 .seen = Fmt.SeenMap.init(gpa),669 .seen = Fmt.SeenMap.init(gpa),
642 .any_error = false,670 .any_error = false,
643 .color = color,671 .color = color,
672 .out_buffer = std.ArrayList(u8).init(gpa),
644 };673 };
674 defer fmt.seen.deinit();
675 defer fmt.out_buffer.deinit();
645676
646 for (input_files.span()) |file_path| {677 for (input_files.span()) |file_path| {
647 try fmtPath(&fmt, file_path, check_flag);678 // Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
679 const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {
680 std.debug.print("unable to open '{}': {}\n", .{ file_path, err });
681 process.exit(1);
682 };
683 defer gpa.free(real_path);
684
685 try fmtPath(&fmt, file_path, check_flag, fs.cwd(), real_path);
648 }686 }
649 if (fmt.any_error) {687 if (fmt.any_error) {
650 process.exit(1);688 process.exit(1);
...@@ -670,48 +708,82 @@ const FmtError = error{...@@ -670,48 +708,82 @@ const FmtError = error{
670 ReadOnlyFileSystem,708 ReadOnlyFileSystem,
671 LinkQuotaExceeded,709 LinkQuotaExceeded,
672 FileBusy,710 FileBusy,
711 EndOfStream,
673} || fs.File.OpenError;712} || fs.File.OpenError;
674713
675fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {714fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
676 // get the real path here to avoid Windows failing on relative file paths with . or .. in them715 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
677 var real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| {716 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
678 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
679 fmt.any_error = true;
680 return;
681 };
682 defer fmt.gpa.free(real_path);
683
684 if (fmt.seen.exists(real_path)) return;
685 try fmt.seen.put(real_path);
686
687 const source_code = fs.cwd().readFileAlloc(fmt.gpa, real_path, max_src_size) catch |err| switch (err) {
688 error.IsDir, error.AccessDenied => {
689 var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
690 defer dir.close();
691
692 var dir_it = dir.iterate();
693
694 while (try dir_it.next()) |entry| {
695 if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
696 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
697 try fmtPath(fmt, full_path, check_mode);
698 }
699 }
700 return;
701 },
702 else => {717 else => {
703 std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });718 std.debug.print("unable to format '{}': {}\n", .{ file_path, err });
704 fmt.any_error = true;719 fmt.any_error = true;
705 return;720 return;
706 },721 },
707 };722 };
708 defer fmt.gpa.free(source_code);723}
709724
710 const tree = std.zig.parse(fmt.gpa, source_code) catch |err| {725fn fmtPathDir(
711 std.debug.warn("error parsing file '{}': {}\n", .{ file_path, err });726 fmt: *Fmt,
712 fmt.any_error = true;727 file_path: []const u8,
713 return;728 check_mode: bool,
729 parent_dir: fs.Dir,
730 parent_sub_path: []const u8,
731) FmtError!void {
732 var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
733 defer dir.close();
734
735 const stat = try dir.stat();
736 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
737
738 var dir_it = dir.iterate();
739 while (try dir_it.next()) |entry| {
740 const is_dir = entry.kind == .Directory;
741 if (is_dir or mem.endsWith(u8, entry.name, ".zig")) {
742 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
743 defer fmt.gpa.free(full_path);
744
745 if (is_dir) {
746 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
747 } else {
748 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
749 std.debug.print("unable to format '{}': {}\n", .{ full_path, err });
750 fmt.any_error = true;
751 return;
752 };
753 }
754 }
755 }
756}
757
758fn fmtPathFile(
759 fmt: *Fmt,
760 file_path: []const u8,
761 check_mode: bool,
762 dir: fs.Dir,
763 sub_path: []const u8,
764) FmtError!void {
765 const source_file = try dir.openFile(sub_path, .{});
766 var file_closed = false;
767 errdefer if (!file_closed) source_file.close();
768
769 const stat = try source_file.stat();
770
771 if (stat.kind == .Directory)
772 return error.IsDir;
773
774 const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) {
775 error.ConnectionResetByPeer => unreachable,
776 error.ConnectionTimedOut => unreachable,
777 else => |e| return e,
714 };778 };
779 source_file.close();
780 file_closed = true;
781 defer fmt.gpa.free(source_code);
782
783 // Add to set after no longer possible to get error.IsDir.
784 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
785
786 const tree = try std.zig.parse(fmt.gpa, source_code);
715 defer tree.deinit();787 defer tree.deinit();
716788
717 for (tree.errors) |parse_error| {789 for (tree.errors) |parse_error| {
...@@ -725,18 +797,23 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {...@@ -725,18 +797,23 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
725 if (check_mode) {797 if (check_mode) {
726 const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);798 const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
727 if (anything_changed) {799 if (anything_changed) {
728 std.debug.warn("{}\n", .{file_path});800 std.debug.print("{}\n", .{file_path});
729 fmt.any_error = true;801 fmt.any_error = true;
730 }802 }
731 } else {803 } else {
732 const baf = try io.BufferedAtomicFile.create(fmt.gpa, fs.cwd(), real_path, .{});804 // As a heuristic, we make enough capacity for the same as the input source.
733 defer baf.destroy();805 try fmt.out_buffer.ensureCapacity(source_code.len);
734806 fmt.out_buffer.items.len = 0;
735 const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);807 const anything_changed = try std.zig.render(fmt.gpa, fmt.out_buffer.writer(), tree);
736 if (anything_changed) {808 if (!anything_changed)
737 std.debug.warn("{}\n", .{file_path});809 return; // Good thing we didn't waste any file system access on this.
738 try baf.finish();810
739 }811 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
812 defer af.deinit();
813
814 try af.file.writeAll(fmt.out_buffer.items);
815 try af.finish();
816 std.debug.print("{}\n", .{file_path});
740 }817 }
741}818}
742819
src-self-hosted/print_targets.zig+1-1
...@@ -62,7 +62,7 @@ pub fn cmdTargets(...@@ -62,7 +62,7 @@ pub fn cmdTargets(
62 allocator: *Allocator,62 allocator: *Allocator,
63 args: []const []const u8,63 args: []const []const u8,
64 /// Output stream64 /// Output stream
65 stdout: var,65 stdout: anytype,
66 native_target: Target,66 native_target: Target,
67) !void {67) !void {
68 const available_glibcs = blk: {68 const available_glibcs = blk: {
src-self-hosted/stage2.zig+4-21
...@@ -653,23 +653,6 @@ export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file:...@@ -653,23 +653,6 @@ export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file:
653 return .None;653 return .None;
654}654}
655655
656fn enumToString(value: var, type_name: []const u8) ![]const u8 {
657 switch (@typeInfo(@TypeOf(value))) {
658 .Enum => |e| {
659 if (e.is_exhaustive) {
660 return std.fmt.allocPrint(std.heap.c_allocator, ".{}", .{@tagName(value)});
661 } else {
662 return std.fmt.allocPrint(
663 std.heap.c_allocator,
664 "@intToEnum({}, {})",
665 .{ type_name, @enumToInt(value) },
666 );
667 }
668 },
669 else => unreachable,
670 }
671}
672
673// ABI warning656// ABI warning
674const Stage2Target = extern struct {657const Stage2Target = extern struct {
675 arch: c_int,658 arch: c_int,
...@@ -887,13 +870,13 @@ const Stage2Target = extern struct {...@@ -887,13 +870,13 @@ const Stage2Target = extern struct {
887870
888 .windows => try os_builtin_str_buffer.outStream().print(871 .windows => try os_builtin_str_buffer.outStream().print(
889 \\ .windows = .{{872 \\ .windows = .{{
890 \\ .min = {},873 \\ .min = {s},
891 \\ .max = {},874 \\ .max = {s},
892 \\ }}}},875 \\ }}}},
893 \\876 \\
894 , .{877 , .{
895 try enumToString(target.os.version_range.windows.min, "Target.Os.WindowsVersion"),878 target.os.version_range.windows.min,
896 try enumToString(target.os.version_range.windows.max, "Target.Os.WindowsVersion"),879 target.os.version_range.windows.max,
897 }),880 }),
898 }881 }
899 try os_builtin_str_buffer.appendSlice("};\n");882 try os_builtin_str_buffer.appendSlice("};\n");
src-self-hosted/test.zig+507-225
...@@ -5,9 +5,10 @@ const Allocator = std.mem.Allocator;...@@ -5,9 +5,10 @@ const Allocator = std.mem.Allocator;
5const zir = @import("zir.zig");5const zir = @import("zir.zig");
6const Package = @import("Package.zig");6const Package = @import("Package.zig");
77
8const cheader = @embedFile("cbe.h");
9
8test "self-hosted" {10test "self-hosted" {
9 var ctx: TestContext = undefined;11 var ctx = TestContext.init();
10 try ctx.init();
11 defer ctx.deinit();12 defer ctx.deinit();
1213
13 try @import("stage2_tests").addCases(&ctx);14 try @import("stage2_tests").addCases(&ctx);
...@@ -15,311 +16,592 @@ test "self-hosted" {...@@ -15,311 +16,592 @@ test "self-hosted" {
15 try ctx.run();16 try ctx.run();
16}17}
1718
19const ErrorMsg = struct {
20 msg: []const u8,
21 line: u32,
22 column: u32,
23};
24
18pub const TestContext = struct {25pub const TestContext = struct {
19 zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase),26 /// TODO: find a way to treat cases as individual tests (shouldn't show "1 test passed" if there are 200 cases)
20 zir_transform_cases: std.ArrayList(ZIRTransformCase),27 cases: std.ArrayList(Case),
28
29 pub const Update = struct {
30 /// The input to the current update. We simulate an incremental update
31 /// with the file's contents changed to this value each update.
32 ///
33 /// This value can change entirely between updates, which would be akin
34 /// to deleting the source file and creating a new one from scratch; or
35 /// you can keep it mostly consistent, with small changes, testing the
36 /// effects of the incremental compilation.
37 src: [:0]const u8,
38 case: union(enum) {
39 /// A transformation update transforms the input and tests against
40 /// the expected output ZIR.
41 Transformation: [:0]const u8,
42 /// An error update attempts to compile bad code, and ensures that it
43 /// fails to compile, and for the expected reasons.
44 /// A slice containing the expected errors *in sequential order*.
45 Error: []const ErrorMsg,
46 /// An execution update compiles and runs the input, testing the
47 /// stdout against the expected results
48 /// This is a slice containing the expected message.
49 Execution: []const u8,
50 },
51 };
2152
22 pub const ZIRCompareOutputCase = struct {53 pub const TestType = enum {
23 name: []const u8,54 Zig,
24 src_list: []const []const u8,55 ZIR,
25 expected_stdout_list: []const []const u8,
26 };56 };
2757
28 pub const ZIRTransformCase = struct {58 /// A Case consists of a set of *updates*. The same Module is used for each
59 /// update, so each update's source is treated as a single file being
60 /// updated by the test harness and incrementally compiled.
61 pub const Case = struct {
62 /// The name of the test case. This is shown if a test fails, and
63 /// otherwise ignored.
29 name: []const u8,64 name: []const u8,
30 cross_target: std.zig.CrossTarget,65 /// The platform the test targets. For non-native platforms, an emulator
66 /// such as QEMU is required for tests to complete.
67 target: std.zig.CrossTarget,
68 /// In order to be able to run e.g. Execution updates, this must be set
69 /// to Executable.
70 output_mode: std.builtin.OutputMode,
31 updates: std.ArrayList(Update),71 updates: std.ArrayList(Update),
72 extension: TestType,
73 cbe: bool = false,
3274
33 pub const Update = struct {75 /// Adds a subcase in which the module is updated with `src`, and the
34 expected: Expected,76 /// resulting ZIR is validated against `result`.
35 src: [:0]const u8,77 pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
36 };78 self.updates.append(.{
37
38 pub const Expected = union(enum) {
39 zir: []const u8,
40 errors: []const []const u8,
41 };
42
43 pub fn addZIR(case: *ZIRTransformCase, src: [:0]const u8, zir_text: []const u8) void {
44 case.updates.append(.{
45 .src = src,79 .src = src,
46 .expected = .{ .zir = zir_text },80 .case = .{ .Transformation = result },
47 }) catch unreachable;81 }) catch unreachable;
48 }82 }
4983
50 pub fn addError(case: *ZIRTransformCase, src: [:0]const u8, errors: []const []const u8) void {84 /// Adds a subcase in which the module is updated with `src`, compiled,
51 case.updates.append(.{85 /// run, and the output is tested against `result`.
86 pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void {
87 self.updates.append(.{
52 .src = src,88 .src = src,
53 .expected = .{ .errors = errors },89 .case = .{ .Execution = result },
54 }) catch unreachable;90 }) catch unreachable;
55 }91 }
92
93 /// Adds a subcase in which the module is updated with `src`, which
94 /// should contain invalid input, and ensures that compilation fails
95 /// for the expected reasons, given in sequential order in `errors` in
96 /// the form `:line:column: error: message`.
97 pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void {
98 var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable;
99 for (errors) |e, i| {
100 if (e[0] != ':') {
101 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
102 }
103 var cur = e[1..];
104 var line_index = std.mem.indexOf(u8, cur, ":");
105 if (line_index == null) {
106 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
107 }
108 const line = std.fmt.parseInt(u32, cur[0..line_index.?], 10) catch @panic("Unable to parse line number");
109 cur = cur[line_index.? + 1 ..];
110 const column_index = std.mem.indexOf(u8, cur, ":");
111 if (column_index == null) {
112 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
113 }
114 const column = std.fmt.parseInt(u32, cur[0..column_index.?], 10) catch @panic("Unable to parse column number");
115 cur = cur[column_index.? + 2 ..];
116 if (!std.mem.eql(u8, cur[0..7], "error: ")) {
117 @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n");
118 }
119 const msg = cur[7..];
120
121 if (line == 0 or column == 0) {
122 @panic("Invalid test: error line and column must be specified starting at one!");
123 }
124
125 array[i] = .{
126 .msg = msg,
127 .line = line - 1,
128 .column = column - 1,
129 };
130 }
131 self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable;
132 }
133
134 /// Adds a subcase in which the module is updated with `src`, and
135 /// asserts that it compiles without issue
136 pub fn compiles(self: *Case, src: [:0]const u8) void {
137 self.addError(src, &[_][]const u8{});
138 }
56 };139 };
57140
58 pub fn addZIRCompareOutput(141 pub fn addExe(
59 ctx: *TestContext,142 ctx: *TestContext,
60 name: []const u8,143 name: []const u8,
61 src_list: []const []const u8,144 target: std.zig.CrossTarget,
62 expected_stdout_list: []const []const u8,145 T: TestType,
63 ) void {146 ) *Case {
64 ctx.zir_cmp_output_cases.append(.{147 ctx.cases.append(Case{
65 .name = name,148 .name = name,
66 .src_list = src_list,149 .target = target,
67 .expected_stdout_list = expected_stdout_list,150 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
151 .output_mode = .Exe,
152 .extension = T,
68 }) catch unreachable;153 }) catch unreachable;
154 return &ctx.cases.items[ctx.cases.items.len - 1];
155 }
156
157 /// Adds a test case for Zig input, producing an executable
158 pub fn exe(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
159 return ctx.addExe(name, target, .Zig);
69 }160 }
70161
71 pub fn addZIRTransform(162 /// Adds a test case for ZIR input, producing an executable
163 pub fn exeZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
164 return ctx.addExe(name, target, .ZIR);
165 }
166
167 pub fn addObj(
72 ctx: *TestContext,168 ctx: *TestContext,
73 name: []const u8,169 name: []const u8,
74 cross_target: std.zig.CrossTarget,170 target: std.zig.CrossTarget,
75 src: [:0]const u8,171 T: TestType,
76 expected_zir: []const u8,172 ) *Case {
77 ) void {173 ctx.cases.append(Case{
78 const case = ctx.zir_transform_cases.addOne() catch unreachable;
79 case.* = .{
80 .name = name,174 .name = name,
81 .cross_target = cross_target,175 .target = target,
82 .updates = std.ArrayList(ZIRTransformCase.Update).init(std.heap.page_allocator),176 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
83 };177 .output_mode = .Obj,
84 case.updates.append(.{178 .extension = T,
85 .src = src,
86 .expected = .{ .zir = expected_zir },
87 }) catch unreachable;179 }) catch unreachable;
180 return &ctx.cases.items[ctx.cases.items.len - 1];
88 }181 }
89182
90 pub fn addZIRMulti(183 /// Adds a test case for Zig input, producing an object file
184 pub fn obj(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
185 return ctx.addObj(name, target, .Zig);
186 }
187
188 /// Adds a test case for ZIR input, producing an object file
189 pub fn objZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {
190 return ctx.addObj(name, target, .ZIR);
191 }
192
193 pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType) *Case {
194 ctx.cases.append(Case{
195 .name = name,
196 .target = target,
197 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
198 .output_mode = .Obj,
199 .extension = T,
200 .cbe = true,
201 }) catch unreachable;
202 return &ctx.cases.items[ctx.cases.items.len - 1];
203 }
204
205 pub fn c(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
206 ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out);
207 }
208
209 pub fn addCompareOutput(
91 ctx: *TestContext,210 ctx: *TestContext,
92 name: []const u8,211 name: []const u8,
93 cross_target: std.zig.CrossTarget,212 T: TestType,
94 ) *ZIRTransformCase {213 src: [:0]const u8,
95 const case = ctx.zir_transform_cases.addOne() catch unreachable;214 expected_stdout: []const u8,
96 case.* = .{215 ) void {
97 .name = name,216 ctx.addExe(name, .{}, T).addCompareOutput(src, expected_stdout);
98 .cross_target = cross_target,217 }
99 .updates = std.ArrayList(ZIRTransformCase.Update).init(std.heap.page_allocator),218
100 };219 /// Adds a test case that compiles the Zig source given in `src`, executes
101 return case;220 /// it, runs it, and tests the output against `expected_stdout`
221 pub fn compareOutput(
222 ctx: *TestContext,
223 name: []const u8,
224 src: [:0]const u8,
225 expected_stdout: []const u8,
226 ) void {
227 return ctx.addCompareOutput(name, .Zig, src, expected_stdout);
228 }
229
230 /// Adds a test case that compiles the ZIR source given in `src`, executes
231 /// it, runs it, and tests the output against `expected_stdout`
232 pub fn compareOutputZIR(
233 ctx: *TestContext,
234 name: []const u8,
235 src: [:0]const u8,
236 expected_stdout: []const u8,
237 ) void {
238 ctx.addCompareOutput(name, .ZIR, src, expected_stdout);
239 }
240
241 pub fn addTransform(
242 ctx: *TestContext,
243 name: []const u8,
244 target: std.zig.CrossTarget,
245 T: TestType,
246 src: [:0]const u8,
247 result: [:0]const u8,
248 ) void {
249 ctx.addObj(name, target, T).addTransform(src, result);
250 }
251
252 /// Adds a test case that compiles the Zig given in `src` to ZIR and tests
253 /// the ZIR against `result`
254 pub fn transform(
255 ctx: *TestContext,
256 name: []const u8,
257 target: std.zig.CrossTarget,
258 src: [:0]const u8,
259 result: [:0]const u8,
260 ) void {
261 ctx.addTransform(name, target, .Zig, src, result);
262 }
263
264 /// Adds a test case that cleans up the ZIR source given in `src`, and
265 /// tests the resulting ZIR against `result`
266 pub fn transformZIR(
267 ctx: *TestContext,
268 name: []const u8,
269 target: std.zig.CrossTarget,
270 src: [:0]const u8,
271 result: [:0]const u8,
272 ) void {
273 ctx.addTransform(name, target, .ZIR, src, result);
274 }
275
276 pub fn addError(
277 ctx: *TestContext,
278 name: []const u8,
279 target: std.zig.CrossTarget,
280 T: TestType,
281 src: [:0]const u8,
282 expected_errors: []const []const u8,
283 ) void {
284 ctx.addObj(name, target, T).addError(src, expected_errors);
285 }
286
287 /// Adds a test case that ensures that the Zig given in `src` fails to
288 /// compile for the expected reasons, given in sequential order in
289 /// `expected_errors` in the form `:line:column: error: message`.
290 pub fn compileError(
291 ctx: *TestContext,
292 name: []const u8,
293 target: std.zig.CrossTarget,
294 src: [:0]const u8,
295 expected_errors: []const []const u8,
296 ) void {
297 ctx.addError(name, target, .Zig, src, expected_errors);
298 }
299
300 /// Adds a test case that ensures that the ZIR given in `src` fails to
301 /// compile for the expected reasons, given in sequential order in
302 /// `expected_errors` in the form `:line:column: error: message`.
303 pub fn compileErrorZIR(
304 ctx: *TestContext,
305 name: []const u8,
306 target: std.zig.CrossTarget,
307 src: [:0]const u8,
308 expected_errors: []const []const u8,
309 ) void {
310 ctx.addError(name, target, .ZIR, src, expected_errors);
311 }
312
313 pub fn addCompiles(
314 ctx: *TestContext,
315 name: []const u8,
316 target: std.zig.CrossTarget,
317 T: TestType,
318 src: [:0]const u8,
319 ) void {
320 ctx.addObj(name, target, T).compiles(src);
102 }321 }
103322
104 fn init(self: *TestContext) !void {323 /// Adds a test case that asserts that the Zig given in `src` compiles
105 self.* = .{324 /// without any errors.
106 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator),325 pub fn compiles(
107 .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(std.heap.page_allocator),326 ctx: *TestContext,
108 };327 name: []const u8,
328 target: std.zig.CrossTarget,
329 src: [:0]const u8,
330 ) void {
331 ctx.addCompiles(name, target, .Zig, src);
332 }
333
334 /// Adds a test case that asserts that the ZIR given in `src` compiles
335 /// without any errors.
336 pub fn compilesZIR(
337 ctx: *TestContext,
338 name: []const u8,
339 target: std.zig.CrossTarget,
340 src: [:0]const u8,
341 ) void {
342 ctx.addCompiles(name, target, .ZIR, src);
343 }
344
345 /// Adds a test case that first ensures that the Zig given in `src` fails
346 /// to compile for the reasons given in sequential order in
347 /// `expected_errors` in the form `:line:column: error: message`, then
348 /// asserts that fixing the source (updating with `fixed_src`) isn't broken
349 /// by incremental compilation.
350 pub fn incrementalFailure(
351 ctx: *TestContext,
352 name: []const u8,
353 target: std.zig.CrossTarget,
354 src: [:0]const u8,
355 expected_errors: []const []const u8,
356 fixed_src: [:0]const u8,
357 ) void {
358 var case = ctx.addObj(name, target, .Zig);
359 case.addError(src, expected_errors);
360 case.compiles(fixed_src);
361 }
362
363 /// Adds a test case that first ensures that the ZIR given in `src` fails
364 /// to compile for the reasons given in sequential order in
365 /// `expected_errors` in the form `:line:column: error: message`, then
366 /// asserts that fixing the source (updating with `fixed_src`) isn't broken
367 /// by incremental compilation.
368 pub fn incrementalFailureZIR(
369 ctx: *TestContext,
370 name: []const u8,
371 target: std.zig.CrossTarget,
372 src: [:0]const u8,
373 expected_errors: []const []const u8,
374 fixed_src: [:0]const u8,
375 ) void {
376 var case = ctx.addObj(name, target, .ZIR);
377 case.addError(src, expected_errors);
378 case.compiles(fixed_src);
379 }
380
381 fn init() TestContext {
382 const allocator = std.heap.page_allocator;
383 return .{ .cases = std.ArrayList(Case).init(allocator) };
109 }384 }
110385
111 fn deinit(self: *TestContext) void {386 fn deinit(self: *TestContext) void {
112 self.zir_cmp_output_cases.deinit();387 for (self.cases.items) |case| {
113 self.zir_transform_cases.deinit();388 for (case.updates.items) |u| {
389 if (u.case == .Error) {
390 case.updates.allocator.free(u.case.Error);
391 }
392 }
393 case.updates.deinit();
394 }
395 self.cases.deinit();
114 self.* = undefined;396 self.* = undefined;
115 }397 }
116398
117 fn run(self: *TestContext) !void {399 fn run(self: *TestContext) !void {
118 var progress = std.Progress{};400 var progress = std.Progress{};
119 const root_node = try progress.start("zir", self.zir_cmp_output_cases.items.len +401 const root_node = try progress.start("tests", self.cases.items.len);
120 self.zir_transform_cases.items.len);
121 defer root_node.end();402 defer root_node.end();
122403
123 const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});404 const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});
124405
125 for (self.zir_cmp_output_cases.items) |case| {406 for (self.cases.items) |case| {
126 std.testing.base_allocator_instance.reset();
127 try self.runOneZIRCmpOutputCase(std.testing.allocator, root_node, case, native_info.target);
128 try std.testing.allocator_instance.validate();
129 }
130 for (self.zir_transform_cases.items) |case| {
131 std.testing.base_allocator_instance.reset();407 std.testing.base_allocator_instance.reset();
132 const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.cross_target);
133 try self.runOneZIRTransformCase(std.testing.allocator, root_node, case, info.target);
134 try std.testing.allocator_instance.validate();
135 }
136 }
137
138 fn runOneZIRCmpOutputCase(
139 self: *TestContext,
140 allocator: *Allocator,
141 root_node: *std.Progress.Node,
142 case: ZIRCompareOutputCase,
143 target: std.Target,
144 ) !void {
145 var tmp = std.testing.tmpDir(.{});
146 defer tmp.cleanup();
147408
148 const tmp_src_path = "test-case.zir";409 var prg_node = root_node.start(case.name, case.updates.items.len);
149 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);410 prg_node.activate();
150 defer root_pkg.destroy();411 defer prg_node.end();
151
152 var prg_node = root_node.start(case.name, case.src_list.len);
153 prg_node.activate();
154 defer prg_node.end();
155
156 var module = try Module.init(allocator, .{
157 .target = target,
158 .output_mode = .Exe,
159 .optimize_mode = .Debug,
160 .bin_file_dir = tmp.dir,
161 .bin_file_path = "a.out",
162 .root_pkg = root_pkg,
163 });
164 defer module.deinit();
165
166 for (case.src_list) |source, i| {
167 var src_node = prg_node.start("update", 2);
168 src_node.activate();
169 defer src_node.end();
170412
171 try tmp.dir.writeFile(tmp_src_path, source);413 // So that we can see which test case failed when the leak checker goes off,
414 // or there's an internal error
415 progress.initial_delay_ns = 0;
416 progress.refresh_rate_ns = 0;
172417
173 var update_node = src_node.start("parse,analysis,codegen", null);418 const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
174 update_node.activate();419 try self.runOneCase(std.testing.allocator, &prg_node, case, info.target);
175 try module.makeBinFileWritable();420 try std.testing.allocator_instance.validate();
176 try module.update();
177 update_node.end();
178
179 var exec_result = x: {
180 var exec_node = src_node.start("execute", null);
181 exec_node.activate();
182 defer exec_node.end();
183
184 try module.makeBinFileExecutable();
185 break :x try std.ChildProcess.exec(.{
186 .allocator = allocator,
187 .argv = &[_][]const u8{"./a.out"},
188 .cwd_dir = tmp.dir,
189 });
190 };
191 defer allocator.free(exec_result.stdout);
192 defer allocator.free(exec_result.stderr);
193 switch (exec_result.term) {
194 .Exited => |code| {
195 if (code != 0) {
196 std.debug.warn("elf file exited with code {}\n", .{code});
197 return error.BinaryBadExitCode;
198 }
199 },
200 else => return error.BinaryCrashed,
201 }
202 const expected_stdout = case.expected_stdout_list[i];
203 if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {
204 std.debug.panic(
205 "update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
206 .{ i, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
207 );
208 }
209 }421 }
210 }422 }
211423
212 fn runOneZIRTransformCase(424 fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case, target: std.Target) !void {
213 self: *TestContext,
214 allocator: *Allocator,
215 root_node: *std.Progress.Node,
216 case: ZIRTransformCase,
217 target: std.Target,
218 ) !void {
219 var tmp = std.testing.tmpDir(.{});425 var tmp = std.testing.tmpDir(.{});
220 defer tmp.cleanup();426 defer tmp.cleanup();
221427
222 var update_node = root_node.start(case.name, case.updates.items.len);428 const tmp_src_path = if (case.extension == .Zig) "test_case.zig" else if (case.extension == .ZIR) "test_case.zir" else unreachable;
223 update_node.activate();
224 defer update_node.end();
225
226 const tmp_src_path = "test-case.zir";
227 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);429 const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
228 defer root_pkg.destroy();430 defer root_pkg.destroy();
229431
432 const bin_name = try std.zig.binNameAlloc(allocator, "test_case", target, case.output_mode, null);
433 defer allocator.free(bin_name);
434
230 var module = try Module.init(allocator, .{435 var module = try Module.init(allocator, .{
231 .target = target,436 .target = target,
232 .output_mode = .Obj,437 // TODO: support tests for object file building, and library builds
438 // and linking. This will require a rework to support multi-file
439 // tests.
440 .output_mode = case.output_mode,
441 // TODO: support testing optimizations
233 .optimize_mode = .Debug,442 .optimize_mode = .Debug,
234 .bin_file_dir = tmp.dir,443 .bin_file_dir = tmp.dir,
235 .bin_file_path = "test-case.o",444 .bin_file_path = bin_name,
236 .root_pkg = root_pkg,445 .root_pkg = root_pkg,
446 .keep_source_files_loaded = true,
447 .object_format = if (case.cbe) .c else null,
237 });448 });
238 defer module.deinit();449 defer module.deinit();
239450
240 for (case.updates.items) |update| {451 for (case.updates.items) |update, update_index| {
241 var prg_node = update_node.start("", 3);452 var update_node = root_node.start("update", 3);
242 prg_node.activate();453 update_node.activate();
243 defer prg_node.end();454 defer update_node.end();
244455
456 var sync_node = update_node.start("write", null);
457 sync_node.activate();
245 try tmp.dir.writeFile(tmp_src_path, update.src);458 try tmp.dir.writeFile(tmp_src_path, update.src);
459 sync_node.end();
246460
247 var module_node = prg_node.start("parse/analysis/codegen", null);461 var module_node = update_node.start("parse/analysis/codegen", null);
248 module_node.activate();462 module_node.activate();
463 try module.makeBinFileWritable();
249 try module.update();464 try module.update();
250 module_node.end();465 module_node.end();
251466
252 switch (update.expected) {467 if (update.case != .Error) {
253 .zir => |expected_zir| {468 var all_errors = try module.getAllErrorsAlloc();
254 var emit_node = prg_node.start("emit", null);469 defer all_errors.deinit(allocator);
255 emit_node.activate();470 if (all_errors.list.len != 0) {
256 var new_zir_module = try zir.emit(allocator, module);471 std.debug.warn("\nErrors occurred updating the module:\n================\n", .{});
257 defer new_zir_module.deinit(allocator);472 for (all_errors.list) |err| {
258 emit_node.end();473 std.debug.warn(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
259474 }
260 var write_node = prg_node.start("write", null);475 std.debug.warn("Test failed.\n", .{});
261 write_node.activate();476 std.process.exit(1);
262 var out_zir = std.ArrayList(u8).init(allocator);477 }
263 defer out_zir.deinit();478 }
264 try new_zir_module.writeToStream(allocator, out_zir.outStream());479
265 write_node.end();480 switch (update.case) {
266481 .Transformation => |expected_output| {
267 std.testing.expectEqualSlices(u8, expected_zir, out_zir.items);482 if (case.cbe) {
483 // The C file is always closed after an update, because we don't support
484 // incremental updates
485 var file = try tmp.dir.openFile(bin_name, .{ .read = true });
486 defer file.close();
487 var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!");
488 defer allocator.free(out);
489
490 if (expected_output.len != out.len) {
491 std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
492 std.process.exit(1);
493 }
494 for (expected_output) |e, i| {
495 if (out[i] != e) {
496 std.debug.warn("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
497 std.process.exit(1);
498 }
499 }
500 } else {
501 update_node.estimated_total_items = 5;
502 var emit_node = update_node.start("emit", null);
503 emit_node.activate();
504 var new_zir_module = try zir.emit(allocator, module);
505 defer new_zir_module.deinit(allocator);
506 emit_node.end();
507
508 var write_node = update_node.start("write", null);
509 write_node.activate();
510 var out_zir = std.ArrayList(u8).init(allocator);
511 defer out_zir.deinit();
512 try new_zir_module.writeToStream(allocator, out_zir.outStream());
513 write_node.end();
514
515 var test_node = update_node.start("assert", null);
516 test_node.activate();
517 defer test_node.end();
518
519 if (expected_output.len != out_zir.items.len) {
520 std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
521 std.process.exit(1);
522 }
523 for (expected_output) |e, i| {
524 if (out_zir.items[i] != e) {
525 std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
526 std.process.exit(1);
527 }
528 }
529 }
268 },530 },
269 .errors => |expected_errors| {531 .Error => |e| {
532 var test_node = update_node.start("assert", null);
533 test_node.activate();
534 defer test_node.end();
535 var handled_errors = try allocator.alloc(bool, e.len);
536 defer allocator.free(handled_errors);
537 for (handled_errors) |*h| {
538 h.* = false;
539 }
270 var all_errors = try module.getAllErrorsAlloc();540 var all_errors = try module.getAllErrorsAlloc();
271 defer all_errors.deinit(module.allocator);541 defer all_errors.deinit(allocator);
272 for (expected_errors) |expected_error| {542 for (all_errors.list) |a| {
273 for (all_errors.list) |full_err_msg| {543 for (e) |ex, i| {
274 const text = try std.fmt.allocPrint(allocator, ":{}:{}: error: {}", .{544 if (a.line == ex.line and a.column == ex.column and std.mem.eql(u8, ex.msg, a.msg)) {
275 full_err_msg.line + 1,545 handled_errors[i] = true;
276 full_err_msg.column + 1,
277 full_err_msg.msg,
278 });
279 defer allocator.free(text);
280 if (std.mem.eql(u8, text, expected_error)) {
281 break;546 break;
282 }547 }
283 } else {548 } else {
284 std.debug.warn(549 std.debug.warn("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg });
285 "{}\nExpected this error:\n================\n{}\n================\nBut found these errors:\n================\n",
286 .{ case.name, expected_error },
287 );
288 for (all_errors.list) |full_err_msg| {
289 std.debug.warn(":{}:{}: error: {}\n", .{
290 full_err_msg.line + 1,
291 full_err_msg.column + 1,
292 full_err_msg.msg,
293 });
294 }
295 std.debug.warn("================\nTest failed\n", .{});
296 std.process.exit(1);550 std.process.exit(1);
297 }551 }
298 }552 }
553
554 for (handled_errors) |h, i| {
555 if (!h) {
556 const er = e[i];
557 std.debug.warn("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg });
558 std.process.exit(1);
559 }
560 }
561 },
562 .Execution => |expected_stdout| {
563 std.debug.assert(!case.cbe);
564
565 update_node.estimated_total_items = 4;
566 var exec_result = x: {
567 var exec_node = update_node.start("execute", null);
568 exec_node.activate();
569 defer exec_node.end();
570
571 try module.makeBinFileExecutable();
572
573 const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
574 defer allocator.free(exe_path);
575
576 break :x try std.ChildProcess.exec(.{
577 .allocator = allocator,
578 .argv = &[_][]const u8{exe_path},
579 .cwd_dir = tmp.dir,
580 });
581 };
582 var test_node = update_node.start("test", null);
583 test_node.activate();
584 defer test_node.end();
585
586 defer allocator.free(exec_result.stdout);
587 defer allocator.free(exec_result.stderr);
588 switch (exec_result.term) {
589 .Exited => |code| {
590 if (code != 0) {
591 std.debug.warn("elf file exited with code {}\n", .{code});
592 return error.BinaryBadExitCode;
593 }
594 },
595 else => return error.BinaryCrashed,
596 }
597 if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {
598 std.debug.panic(
599 "update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
600 .{ update_index, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
601 );
602 }
299 },603 },
300 }604 }
301 }605 }
302 }606 }
303};607};
304
305fn debugPrintErrors(src: []const u8, errors: var) void {
306 std.debug.warn("\n", .{});
307 var nl = true;
308 var line: usize = 1;
309 for (src) |byte| {
310 if (nl) {
311 std.debug.warn("{: >3}| ", .{line});
312 nl = false;
313 }
314 if (byte == '\n') {
315 nl = true;
316 line += 1;
317 }
318 std.debug.warn("{c}", .{byte});
319 }
320 std.debug.warn("\n", .{});
321 for (errors) |err_msg| {
322 const loc = std.zig.findLineColumn(src, err_msg.byte_offset);
323 std.debug.warn("{}:{}: error: {}\n", .{ loc.line + 1, loc.column + 1, err_msg.msg });
324 }
325}
src-self-hosted/tracy.zig created+45
...@@ -0,0 +1,45 @@
1pub const std = @import("std");
2
3pub const enable = if (std.builtin.is_test) false else @import("build_options").enable_tracy;
4
5extern fn ___tracy_emit_zone_begin_callstack(
6 srcloc: *const ___tracy_source_location_data,
7 depth: c_int,
8 active: c_int,
9) ___tracy_c_zone_context;
10
11extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void;
12
13pub const ___tracy_source_location_data = extern struct {
14 name: ?[*:0]const u8,
15 function: [*:0]const u8,
16 file: [*:0]const u8,
17 line: u32,
18 color: u32,
19};
20
21pub const ___tracy_c_zone_context = extern struct {
22 id: u32,
23 active: c_int,
24
25 pub fn end(self: ___tracy_c_zone_context) void {
26 ___tracy_emit_zone_end(self);
27 }
28};
29
30pub const Ctx = if (enable) ___tracy_c_zone_context else struct {
31 pub fn end(self: Ctx) void {}
32};
33
34pub inline fn trace(comptime src: std.builtin.SourceLocation) Ctx {
35 if (!enable) return .{};
36
37 const loc: ___tracy_source_location_data = .{
38 .name = null,
39 .function = src.fn_name.ptr,
40 .file = src.file.ptr,
41 .line = src.line,
42 .color = 0,
43 };
44 return ___tracy_emit_zone_begin_callstack(&loc, 1, 1);
45}
src-self-hosted/translate_c.zig+577-514
...@@ -20,7 +20,7 @@ pub const Error = error{OutOfMemory};...@@ -20,7 +20,7 @@ pub const Error = error{OutOfMemory};
20const TypeError = Error || error{UnsupportedType};20const TypeError = Error || error{UnsupportedType};
21const TransError = TypeError || error{UnsupportedTranslation};21const TransError = TypeError || error{UnsupportedTranslation};
2222
23const DeclTable = std.HashMap(usize, []const u8, addrHash, addrEql);23const DeclTable = std.HashMap(usize, []const u8, addrHash, addrEql, false);
2424
25fn addrHash(x: usize) u32 {25fn addrHash(x: usize) u32 {
26 switch (@typeInfo(usize).Int.bits) {26 switch (@typeInfo(usize).Int.bits) {
...@@ -586,11 +586,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {...@@ -586,11 +586,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
586 for (proto_node.params()) |*param, i| {586 for (proto_node.params()) |*param, i| {
587 const param_name = if (param.name_token) |name_tok|587 const param_name = if (param.name_token) |name_tok|
588 tokenSlice(c, name_tok)588 tokenSlice(c, name_tok)
589 else if (param.param_type == .var_args) {589 else
590 assert(i + 1 == proto_node.params_len);
591 proto_node.params_len -= 1;
592 break;
593 } else
594 return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name});590 return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name});
595591
596 const c_param = ZigClangFunctionDecl_getParamDecl(fn_decl, param_id);592 const c_param = ZigClangFunctionDecl_getParamDecl(fn_decl, param_id);
...@@ -602,10 +598,20 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {...@@ -602,10 +598,20 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
602 if (!is_const) {598 if (!is_const) {
603 const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{}", .{mangled_param_name});599 const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{}", .{mangled_param_name});
604 const arg_name = try block_scope.makeMangledName(c, bare_arg_name);600 const arg_name = try block_scope.makeMangledName(c, bare_arg_name);
605 const node = try transCreateNodeVarDecl(c, false, false, mangled_param_name);601
606 node.eq_token = try appendToken(c, .Equal, "=");602 const mut_tok = try appendToken(c, .Keyword_var, "var");
607 node.init_node = try transCreateNodeIdentifier(c, arg_name);603 const name_tok = try appendIdentifier(c, mangled_param_name);
608 node.semicolon_token = try appendToken(c, .Semicolon, ";");604 const eq_token = try appendToken(c, .Equal, "=");
605 const init_node = try transCreateNodeIdentifier(c, arg_name);
606 const semicolon_token = try appendToken(c, .Semicolon, ";");
607 const node = try ast.Node.VarDecl.create(c.arena, .{
608 .mut_token = mut_tok,
609 .name_token = name_tok,
610 .semicolon_token = semicolon_token,
611 }, .{
612 .eq_token = eq_token,
613 .init_node = init_node,
614 });
609 try block_scope.statements.append(&node.base);615 try block_scope.statements.append(&node.base);
610 param.name_token = try appendIdentifier(c, arg_name);616 param.name_token = try appendIdentifier(c, arg_name);
611 _ = try appendToken(c, .Colon, ":");617 _ = try appendToken(c, .Colon, ":");
...@@ -622,7 +628,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {...@@ -622,7 +628,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
622 => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}),628 => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}),
623 };629 };
624 const body_node = try block_scope.complete(rp.c);630 const body_node = try block_scope.complete(rp.c);
625 proto_node.body_node = &body_node.base;631 proto_node.setTrailer("body_node", &body_node.base);
626 return addTopLevelDecl(c, fn_name, &proto_node.base);632 return addTopLevelDecl(c, fn_name, &proto_node.base);
627}633}
628634
...@@ -725,23 +731,20 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {...@@ -725,23 +731,20 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void {
725 break :blk null;731 break :blk null;
726 };732 };
727733
728 const node = try c.arena.create(ast.Node.VarDecl);734 const node = try ast.Node.VarDecl.create(c.arena, .{
729 node.* = .{735 .name_token = name_tok,
730 .doc_comments = null,736 .mut_token = mut_tok,
737 .semicolon_token = try appendToken(c, .Semicolon, ";"),
738 }, .{
731 .visib_token = visib_tok,739 .visib_token = visib_tok,
732 .thread_local_token = thread_local_token,740 .thread_local_token = thread_local_token,
733 .name_token = name_tok,
734 .eq_token = eq_tok,741 .eq_token = eq_tok,
735 .mut_token = mut_tok,
736 .comptime_token = null,
737 .extern_export_token = extern_tok,742 .extern_export_token = extern_tok,
738 .lib_name = null,
739 .type_node = type_node,743 .type_node = type_node,
740 .align_node = align_expr,744 .align_node = align_expr,
741 .section_node = linksection_expr,745 .section_node = linksection_expr,
742 .init_node = init_node,746 .init_node = init_node,
743 .semicolon_token = try appendToken(c, .Semicolon, ";"),747 });
744 };
745 return addTopLevelDecl(c, checked_name, &node.base);748 return addTopLevelDecl(c, checked_name, &node.base);
746}749}
747750
...@@ -776,8 +779,8 @@ fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 {...@@ -776,8 +779,8 @@ fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 {
776}779}
777780
778fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_level_visit: bool) Error!?*ast.Node {781fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_level_visit: bool) Error!?*ast.Node {
779 if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |kv|782 if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |name|
780 return transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice783 return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
781 const rp = makeRestorePoint(c);784 const rp = makeRestorePoint(c);
782785
783 const typedef_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, typedef_decl)));786 const typedef_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, typedef_decl)));
...@@ -795,31 +798,46 @@ fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_l...@@ -795,31 +798,46 @@ fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_l
795798
796 _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), checked_name);799 _ = try c.decl_table.put(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)), checked_name);
797 const node = (try transCreateNodeTypedef(rp, typedef_decl, true, checked_name)) orelse return null;800 const node = (try transCreateNodeTypedef(rp, typedef_decl, true, checked_name)) orelse return null;
798 try addTopLevelDecl(c, checked_name, &node.base);801 try addTopLevelDecl(c, checked_name, node);
799 return transCreateNodeIdentifier(c, checked_name);802 return transCreateNodeIdentifier(c, checked_name);
800}803}
801804
802fn transCreateNodeTypedef(rp: RestorePoint, typedef_decl: *const ZigClangTypedefNameDecl, toplevel: bool, checked_name: []const u8) Error!?*ast.Node.VarDecl {805fn transCreateNodeTypedef(
803 const node = try transCreateNodeVarDecl(rp.c, toplevel, true, checked_name);806 rp: RestorePoint,
804 node.eq_token = try appendToken(rp.c, .Equal, "=");807 typedef_decl: *const ZigClangTypedefNameDecl,
805808 toplevel: bool,
809 checked_name: []const u8,
810) Error!?*ast.Node {
811 const visib_tok = if (toplevel) try appendToken(rp.c, .Keyword_pub, "pub") else null;
812 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
813 const name_tok = try appendIdentifier(rp.c, checked_name);
814 const eq_token = try appendToken(rp.c, .Equal, "=");
806 const child_qt = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);815 const child_qt = ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl);
807 const typedef_loc = ZigClangTypedefNameDecl_getLocation(typedef_decl);816 const typedef_loc = ZigClangTypedefNameDecl_getLocation(typedef_decl);
808 node.init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {817 const init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {
809 error.UnsupportedType => {818 error.UnsupportedType => {
810 try failDecl(rp.c, typedef_loc, checked_name, "unable to resolve typedef child type", .{});819 try failDecl(rp.c, typedef_loc, checked_name, "unable to resolve typedef child type", .{});
811 return null;820 return null;
812 },821 },
813 error.OutOfMemory => |e| return e,822 error.OutOfMemory => |e| return e,
814 };823 };
824 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
815825
816 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");826 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
817 return node;827 .name_token = name_tok,
828 .mut_token = mut_tok,
829 .semicolon_token = semicolon_token,
830 }, .{
831 .visib_token = visib_tok,
832 .eq_token = eq_token,
833 .init_node = init_node,
834 });
835 return &node.base;
818}836}
819837
820fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node {838fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node {
821 if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |kv|839 if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |name|
822 return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice840 return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
823 const record_loc = ZigClangRecordDecl_getLocation(record_decl);841 const record_loc = ZigClangRecordDecl_getLocation(record_decl);
824842
825 var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, record_decl)));843 var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, record_decl)));
...@@ -847,12 +865,14 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*...@@ -847,12 +865,14 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
847 const name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ container_kind_name, bare_name });865 const name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ container_kind_name, bare_name });
848 _ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name);866 _ = try c.decl_table.put(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)), name);
849867
850 const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name);868 const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
869 const mut_tok = try appendToken(c, .Keyword_const, "const");
870 const name_tok = try appendIdentifier(c, name);
851871
852 node.eq_token = try appendToken(c, .Equal, "=");872 const eq_token = try appendToken(c, .Equal, "=");
853873
854 var semicolon: ast.TokenIndex = undefined;874 var semicolon: ast.TokenIndex = undefined;
855 node.init_node = blk: {875 const init_node = blk: {
856 const rp = makeRestorePoint(c);876 const rp = makeRestorePoint(c);
857 const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse {877 const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse {
858 const opaque = try transCreateNodeOpaqueType(c);878 const opaque = try transCreateNodeOpaqueType(c);
...@@ -959,7 +979,16 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*...@@ -959,7 +979,16 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
959 semicolon = try appendToken(c, .Semicolon, ";");979 semicolon = try appendToken(c, .Semicolon, ";");
960 break :blk &container_node.base;980 break :blk &container_node.base;
961 };981 };
962 node.semicolon_token = semicolon;982
983 const node = try ast.Node.VarDecl.create(c.arena, .{
984 .name_token = name_tok,
985 .mut_token = mut_tok,
986 .semicolon_token = semicolon,
987 }, .{
988 .visib_token = visib_tok,
989 .eq_token = eq_token,
990 .init_node = init_node,
991 });
963992
964 try addTopLevelDecl(c, name, &node.base);993 try addTopLevelDecl(c, name, &node.base);
965 if (!is_unnamed)994 if (!is_unnamed)
...@@ -969,7 +998,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*...@@ -969,7 +998,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*
969998
970fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node {999fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node {
971 if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name|1000 if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name|
972 return try transCreateNodeIdentifier(c, name.value); // Avoid processing this decl twice1001 return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
973 const rp = makeRestorePoint(c);1002 const rp = makeRestorePoint(c);
974 const enum_loc = ZigClangEnumDecl_getLocation(enum_decl);1003 const enum_loc = ZigClangEnumDecl_getLocation(enum_decl);
9751004
...@@ -982,10 +1011,13 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No...@@ -982,10 +1011,13 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
9821011
983 const name = try std.fmt.allocPrint(c.arena, "enum_{}", .{bare_name});1012 const name = try std.fmt.allocPrint(c.arena, "enum_{}", .{bare_name});
984 _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name);1013 _ = try c.decl_table.put(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)), name);
985 const node = try transCreateNodeVarDecl(c, !is_unnamed, true, name);
986 node.eq_token = try appendToken(c, .Equal, "=");
9871014
988 node.init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: {1015 const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
1016 const mut_tok = try appendToken(c, .Keyword_const, "const");
1017 const name_tok = try appendIdentifier(c, name);
1018 const eq_token = try appendToken(c, .Equal, "=");
1019
1020 const init_node = if (ZigClangEnumDecl_getDefinition(enum_decl)) |enum_def| blk: {
989 var pure_enum = true;1021 var pure_enum = true;
990 var it = ZigClangEnumDecl_enumerator_begin(enum_def);1022 var it = ZigClangEnumDecl_enumerator_begin(enum_def);
991 var end_it = ZigClangEnumDecl_enumerator_end(enum_def);1023 var end_it = ZigClangEnumDecl_enumerator_end(enum_def);
...@@ -1063,23 +1095,34 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No...@@ -1063,23 +1095,34 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
10631095
1064 // In C each enum value is in the global namespace. So we put them there too.1096 // In C each enum value is in the global namespace. So we put them there too.
1065 // At this point we can rely on the enum emitting successfully.1097 // At this point we can rely on the enum emitting successfully.
1066 const tld_node = try transCreateNodeVarDecl(c, true, true, enum_val_name);1098 const tld_visib_tok = try appendToken(c, .Keyword_pub, "pub");
1067 tld_node.eq_token = try appendToken(c, .Equal, "=");1099 const tld_mut_tok = try appendToken(c, .Keyword_const, "const");
1100 const tld_name_tok = try appendIdentifier(c, enum_val_name);
1101 const tld_eq_token = try appendToken(c, .Equal, "=");
1068 const cast_node = try rp.c.createBuiltinCall("@enumToInt", 1);1102 const cast_node = try rp.c.createBuiltinCall("@enumToInt", 1);
1069 const enum_ident = try transCreateNodeIdentifier(c, name);1103 const enum_ident = try transCreateNodeIdentifier(c, name);
1070 const period_tok = try appendToken(c, .Period, ".");1104 const period_tok = try appendToken(c, .Period, ".");
1071 const field_ident = try transCreateNodeIdentifier(c, field_name);1105 const field_ident = try transCreateNodeIdentifier(c, field_name);
1072 const field_access_node = try c.arena.create(ast.Node.InfixOp);1106 const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
1073 field_access_node.* = .{1107 field_access_node.* = .{
1108 .base = .{ .tag = .Period },
1074 .op_token = period_tok,1109 .op_token = period_tok,
1075 .lhs = enum_ident,1110 .lhs = enum_ident,
1076 .op = .Period,
1077 .rhs = field_ident,1111 .rhs = field_ident,
1078 };1112 };
1079 cast_node.params()[0] = &field_access_node.base;1113 cast_node.params()[0] = &field_access_node.base;
1080 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");1114 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
1081 tld_node.init_node = &cast_node.base;1115 const tld_init_node = &cast_node.base;
1082 tld_node.semicolon_token = try appendToken(c, .Semicolon, ";");1116 const tld_semicolon_token = try appendToken(c, .Semicolon, ";");
1117 const tld_node = try ast.Node.VarDecl.create(c.arena, .{
1118 .name_token = tld_name_tok,
1119 .mut_token = tld_mut_tok,
1120 .semicolon_token = tld_semicolon_token,
1121 }, .{
1122 .visib_token = tld_visib_tok,
1123 .eq_token = tld_eq_token,
1124 .init_node = tld_init_node,
1125 });
1083 try addTopLevelDecl(c, field_name, &tld_node.base);1126 try addTopLevelDecl(c, field_name, &tld_node.base);
1084 }1127 }
1085 // make non exhaustive1128 // make non exhaustive
...@@ -1109,7 +1152,16 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No...@@ -1109,7 +1152,16 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
1109 } else1152 } else
1110 try transCreateNodeOpaqueType(c);1153 try transCreateNodeOpaqueType(c);
11111154
1112 node.semicolon_token = try appendToken(c, .Semicolon, ";");1155 const semicolon_token = try appendToken(c, .Semicolon, ";");
1156 const node = try ast.Node.VarDecl.create(c.arena, .{
1157 .name_token = name_tok,
1158 .mut_token = mut_tok,
1159 .semicolon_token = semicolon_token,
1160 }, .{
1161 .visib_token = visib_tok,
1162 .eq_token = eq_token,
1163 .init_node = init_node,
1164 });
11131165
1114 try addTopLevelDecl(c, name, &node.base);1166 try addTopLevelDecl(c, name, &node.base);
1115 if (!is_unnamed)1167 if (!is_unnamed)
...@@ -1117,11 +1169,23 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No...@@ -1117,11 +1169,23 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
1117 return transCreateNodeIdentifier(c, name);1169 return transCreateNodeIdentifier(c, name);
1118}1170}
11191171
1120fn createAlias(c: *Context, alias: var) !void {1172fn createAlias(c: *Context, alias: anytype) !void {
1121 const node = try transCreateNodeVarDecl(c, true, true, alias.alias);1173 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
1122 node.eq_token = try appendToken(c, .Equal, "=");1174 const mut_tok = try appendToken(c, .Keyword_const, "const");
1123 node.init_node = try transCreateNodeIdentifier(c, alias.name);1175 const name_tok = try appendIdentifier(c, alias.alias);
1124 node.semicolon_token = try appendToken(c, .Semicolon, ";");1176 const eq_token = try appendToken(c, .Equal, "=");
1177 const init_node = try transCreateNodeIdentifier(c, alias.name);
1178 const semicolon_token = try appendToken(c, .Semicolon, ";");
1179
1180 const node = try ast.Node.VarDecl.create(c.arena, .{
1181 .name_token = name_tok,
1182 .mut_token = mut_tok,
1183 .semicolon_token = semicolon_token,
1184 }, .{
1185 .visib_token = visib_tok,
1186 .eq_token = eq_token,
1187 .init_node = init_node,
1188 });
1125 return addTopLevelDecl(c, alias.alias, &node.base);1189 return addTopLevelDecl(c, alias.alias, &node.base);
1126}1190}
11271191
...@@ -1155,7 +1219,7 @@ fn transStmt(...@@ -1155,7 +1219,7 @@ fn transStmt(
1155 .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used),1219 .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used),
1156 .ParenExprClass => {1220 .ParenExprClass => {
1157 const expr = try transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), .used, lrvalue);1221 const expr = try transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), .used, lrvalue);
1158 if (expr.id == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);1222 if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
1159 const node = try rp.c.arena.create(ast.Node.GroupedExpression);1223 const node = try rp.c.arena.create(ast.Node.GroupedExpression);
1160 node.* = .{1224 node.* = .{
1161 .lparen = try appendToken(rp.c, .LParen, "("),1225 .lparen = try appendToken(rp.c, .LParen, "("),
...@@ -1200,7 +1264,7 @@ fn transStmt(...@@ -1200,7 +1264,7 @@ fn transStmt(
1200 .OpaqueValueExprClass => {1264 .OpaqueValueExprClass => {
1201 const source_expr = ZigClangOpaqueValueExpr_getSourceExpr(@ptrCast(*const ZigClangOpaqueValueExpr, stmt)).?;1265 const source_expr = ZigClangOpaqueValueExpr_getSourceExpr(@ptrCast(*const ZigClangOpaqueValueExpr, stmt)).?;
1202 const expr = try transExpr(rp, scope, source_expr, .used, lrvalue);1266 const expr = try transExpr(rp, scope, source_expr, .used, lrvalue);
1203 if (expr.id == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);1267 if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
1204 const node = try rp.c.arena.create(ast.Node.GroupedExpression);1268 const node = try rp.c.arena.create(ast.Node.GroupedExpression);
1205 node.* = .{1269 node.* = .{
1206 .lparen = try appendToken(rp.c, .LParen, "("),1270 .lparen = try appendToken(rp.c, .LParen, "("),
...@@ -1230,7 +1294,7 @@ fn transBinaryOperator(...@@ -1230,7 +1294,7 @@ fn transBinaryOperator(
1230 const op = ZigClangBinaryOperator_getOpcode(stmt);1294 const op = ZigClangBinaryOperator_getOpcode(stmt);
1231 const qt = ZigClangBinaryOperator_getType(stmt);1295 const qt = ZigClangBinaryOperator_getType(stmt);
1232 var op_token: ast.TokenIndex = undefined;1296 var op_token: ast.TokenIndex = undefined;
1233 var op_id: ast.Node.InfixOp.Op = undefined;1297 var op_id: ast.Node.Tag = undefined;
1234 switch (op) {1298 switch (op) {
1235 .Assign => return try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)),1299 .Assign => return try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)),
1236 .Comma => {1300 .Comma => {
...@@ -1461,13 +1525,17 @@ fn transDeclStmtOne(...@@ -1461,13 +1525,17 @@ fn transDeclStmtOne(
1461 @ptrCast(*const ZigClangNamedDecl, var_decl),1525 @ptrCast(*const ZigClangNamedDecl, var_decl),
1462 ));1526 ));
1463 const mangled_name = try block_scope.makeMangledName(c, name);1527 const mangled_name = try block_scope.makeMangledName(c, name);
1464 const node = try transCreateNodeVarDecl(c, false, ZigClangQualType_isConstQualified(qual_type), mangled_name);1528 const mut_tok = if (ZigClangQualType_isConstQualified(qual_type))
1529 try appendToken(c, .Keyword_const, "const")
1530 else
1531 try appendToken(c, .Keyword_var, "var");
1532 const name_tok = try appendIdentifier(c, mangled_name);
14651533
1466 _ = try appendToken(c, .Colon, ":");1534 _ = try appendToken(c, .Colon, ":");
1467 const loc = ZigClangDecl_getLocation(decl);1535 const loc = ZigClangDecl_getLocation(decl);
1468 node.type_node = try transQualType(rp, qual_type, loc);1536 const type_node = try transQualType(rp, qual_type, loc);
14691537
1470 node.eq_token = try appendToken(c, .Equal, "=");1538 const eq_token = try appendToken(c, .Equal, "=");
1471 var init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|1539 var init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr|
1472 try transExprCoercing(rp, scope, expr, .used, .r_value)1540 try transExprCoercing(rp, scope, expr, .used, .r_value)
1473 else1541 else
...@@ -1478,8 +1546,17 @@ fn transDeclStmtOne(...@@ -1478,8 +1546,17 @@ fn transDeclStmtOne(
1478 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");1546 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
1479 init_node = &builtin_node.base;1547 init_node = &builtin_node.base;
1480 }1548 }
1481 node.init_node = init_node;1549 const semicolon_token = try appendToken(c, .Semicolon, ";");
1482 node.semicolon_token = try appendToken(c, .Semicolon, ";");1550 const node = try ast.Node.VarDecl.create(c.arena, .{
1551 .name_token = name_tok,
1552 .mut_token = mut_tok,
1553 .semicolon_token = semicolon_token,
1554 }, .{
1555 .thread_local_token = thread_local_token,
1556 .eq_token = eq_token,
1557 .type_node = type_node,
1558 .init_node = init_node,
1559 });
1483 return &node.base;1560 return &node.base;
1484 },1561 },
1485 .Typedef => {1562 .Typedef => {
...@@ -1494,7 +1571,7 @@ fn transDeclStmtOne(...@@ -1494,7 +1571,7 @@ fn transDeclStmtOne(
1494 const mangled_name = try block_scope.makeMangledName(c, name);1571 const mangled_name = try block_scope.makeMangledName(c, name);
1495 const node = (try transCreateNodeTypedef(rp, typedef_decl, false, mangled_name)) orelse1572 const node = (try transCreateNodeTypedef(rp, typedef_decl, false, mangled_name)) orelse
1496 return error.UnsupportedTranslation;1573 return error.UnsupportedTranslation;
1497 return &node.base;1574 return node;
1498 },1575 },
1499 else => |kind| return revertAndWarn(1576 else => |kind| return revertAndWarn(
1500 rp,1577 rp,
...@@ -1561,7 +1638,7 @@ fn transImplicitCastExpr(...@@ -1561,7 +1638,7 @@ fn transImplicitCastExpr(
1561 return maybeSuppressResult(rp, scope, result_used, sub_expr_node);1638 return maybeSuppressResult(rp, scope, result_used, sub_expr_node);
1562 }1639 }
15631640
1564 const prefix_op = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");1641 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
1565 prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value);1642 prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value);
15661643
1567 return maybeSuppressResult(rp, scope, result_used, &prefix_op.base);1644 return maybeSuppressResult(rp, scope, result_used, &prefix_op.base);
...@@ -1616,7 +1693,7 @@ fn transBoolExpr(...@@ -1616,7 +1693,7 @@ fn transBoolExpr(
1616 var res = try transExpr(rp, scope, expr, used, lrvalue);1693 var res = try transExpr(rp, scope, expr, used, lrvalue);
16171694
1618 if (isBoolRes(res)) {1695 if (isBoolRes(res)) {
1619 if (!grouped and res.id == .GroupedExpression) {1696 if (!grouped and res.tag == .GroupedExpression) {
1620 const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res);1697 const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res);
1621 res = group.expr;1698 res = group.expr;
1622 // get zig fmt to work properly1699 // get zig fmt to work properly
...@@ -1659,30 +1736,23 @@ fn exprIsStringLiteral(expr: *const ZigClangExpr) bool {...@@ -1659,30 +1736,23 @@ fn exprIsStringLiteral(expr: *const ZigClangExpr) bool {
1659}1736}
16601737
1661fn isBoolRes(res: *ast.Node) bool {1738fn isBoolRes(res: *ast.Node) bool {
1662 switch (res.id) {1739 switch (res.tag) {
1663 .InfixOp => switch (@fieldParentPtr(ast.Node.InfixOp, "base", res).op) {1740 .BoolOr,
1664 .BoolOr,1741 .BoolAnd,
1665 .BoolAnd,1742 .EqualEqual,
1666 .EqualEqual,1743 .BangEqual,
1667 .BangEqual,1744 .LessThan,
1668 .LessThan,1745 .GreaterThan,
1669 .GreaterThan,1746 .LessOrEqual,
1670 .LessOrEqual,1747 .GreaterOrEqual,
1671 .GreaterOrEqual,1748 .BoolNot,
1672 => return true,1749 .BoolLiteral,
1750 => return true,
16731751
1674 else => {},
1675 },
1676 .PrefixOp => switch (@fieldParentPtr(ast.Node.PrefixOp, "base", res).op) {
1677 .BoolNot => return true,
1678
1679 else => {},
1680 },
1681 .BoolLiteral => return true,
1682 .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),1752 .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
1683 else => {},1753
1754 else => return false,
1684 }1755 }
1685 return false;
1686}1756}
16871757
1688fn finishBoolExpr(1758fn finishBoolExpr(
...@@ -2130,7 +2200,7 @@ fn transInitListExprRecord(...@@ -2130,7 +2200,7 @@ fn transInitListExprRecord(
2130 var raw_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl)));2200 var raw_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl)));
2131 if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) {2201 if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) {
2132 const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?;2202 const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?;
2133 raw_name = try mem.dupe(rp.c.arena, u8, name.value);2203 raw_name = try mem.dupe(rp.c.arena, u8, name);
2134 }2204 }
2135 const field_name_tok = try appendIdentifier(rp.c, raw_name);2205 const field_name_tok = try appendIdentifier(rp.c, raw_name);
21362206
...@@ -2161,22 +2231,17 @@ fn transCreateNodeArrayType(...@@ -2161,22 +2231,17 @@ fn transCreateNodeArrayType(
2161 rp: RestorePoint,2231 rp: RestorePoint,
2162 source_loc: ZigClangSourceLocation,2232 source_loc: ZigClangSourceLocation,
2163 ty: *const ZigClangType,2233 ty: *const ZigClangType,
2164 len: var,2234 len: anytype,
2165) TransError!*ast.Node {2235) !*ast.Node {
2166 var node = try transCreateNodePrefixOp(2236 const node = try rp.c.arena.create(ast.Node.ArrayType);
2167 rp.c,2237 const op_token = try appendToken(rp.c, .LBracket, "[");
2168 .{2238 const len_expr = try transCreateNodeInt(rp.c, len);
2169 .ArrayType = .{
2170 .len_expr = undefined,
2171 .sentinel = null,
2172 },
2173 },
2174 .LBracket,
2175 "[",
2176 );
2177 node.op.ArrayType.len_expr = try transCreateNodeInt(rp.c, len);
2178 _ = try appendToken(rp.c, .RBracket, "]");2239 _ = try appendToken(rp.c, .RBracket, "]");
2179 node.rhs = try transType(rp, ty, source_loc);2240 node.* = .{
2241 .op_token = op_token,
2242 .rhs = try transType(rp, ty, source_loc),
2243 .len_expr = len_expr,
2244 };
2180 return &node.base;2245 return &node.base;
2181}2246}
21822247
...@@ -2244,11 +2309,11 @@ fn transInitListExprArray(...@@ -2244,11 +2309,11 @@ fn transInitListExprArray(
2244 &filler_init_node.base2309 &filler_init_node.base
2245 else blk: {2310 else blk: {
2246 const mul_tok = try appendToken(rp.c, .AsteriskAsterisk, "**");2311 const mul_tok = try appendToken(rp.c, .AsteriskAsterisk, "**");
2247 const mul_node = try rp.c.arena.create(ast.Node.InfixOp);2312 const mul_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
2248 mul_node.* = .{2313 mul_node.* = .{
2314 .base = .{ .tag = .ArrayMult },
2249 .op_token = mul_tok,2315 .op_token = mul_tok,
2250 .lhs = &filler_init_node.base,2316 .lhs = &filler_init_node.base,
2251 .op = .ArrayMult,
2252 .rhs = try transCreateNodeInt(rp.c, leftover_count),2317 .rhs = try transCreateNodeInt(rp.c, leftover_count),
2253 };2318 };
2254 break :blk &mul_node.base;2319 break :blk &mul_node.base;
...@@ -2258,11 +2323,11 @@ fn transInitListExprArray(...@@ -2258,11 +2323,11 @@ fn transInitListExprArray(
2258 return rhs_node;2323 return rhs_node;
2259 }2324 }
22602325
2261 const cat_node = try rp.c.arena.create(ast.Node.InfixOp);2326 const cat_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
2262 cat_node.* = .{2327 cat_node.* = .{
2328 .base = .{ .tag = .ArrayCat },
2263 .op_token = cat_tok,2329 .op_token = cat_tok,
2264 .lhs = &init_node.base,2330 .lhs = &init_node.base,
2265 .op = .ArrayCat,
2266 .rhs = rhs_node,2331 .rhs = rhs_node,
2267 };2332 };
2268 return &cat_node.base;2333 return &cat_node.base;
...@@ -2449,7 +2514,7 @@ fn transDoWhileLoop(...@@ -2449,7 +2514,7 @@ fn transDoWhileLoop(
2449 },2514 },
2450 };2515 };
2451 defer cond_scope.deinit();2516 defer cond_scope.deinit();
2452 const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");2517 const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
2453 prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);2518 prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
2454 _ = try appendToken(rp.c, .RParen, ")");2519 _ = try appendToken(rp.c, .RParen, ")");
2455 if_node.condition = &prefix_op.base;2520 if_node.condition = &prefix_op.base;
...@@ -2655,11 +2720,11 @@ fn transCase(...@@ -2655,11 +2720,11 @@ fn transCase(
2655 const ellips = try appendToken(rp.c, .Ellipsis3, "...");2720 const ellips = try appendToken(rp.c, .Ellipsis3, "...");
2656 const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);2721 const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
26572722
2658 const node = try rp.c.arena.create(ast.Node.InfixOp);2723 const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
2659 node.* = .{2724 node.* = .{
2725 .base = .{ .tag = .Range },
2660 .op_token = ellips,2726 .op_token = ellips,
2661 .lhs = lhs_node,2727 .lhs = lhs_node,
2662 .op = .Range,
2663 .rhs = rhs_node,2728 .rhs = rhs_node,
2664 };2729 };
2665 break :blk &node.base;2730 break :blk &node.base;
...@@ -2855,7 +2920,7 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberE...@@ -2855,7 +2920,7 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberE
2855 const field_decl = @ptrCast(*const struct_ZigClangFieldDecl, member_decl);2920 const field_decl = @ptrCast(*const struct_ZigClangFieldDecl, member_decl);
2856 if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) {2921 if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) {
2857 const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?;2922 const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?;
2858 break :blk try mem.dupe(rp.c.arena, u8, name.value);2923 break :blk try mem.dupe(rp.c.arena, u8, name);
2859 }2924 }
2860 }2925 }
2861 const decl = @ptrCast(*const ZigClangNamedDecl, member_decl);2926 const decl = @ptrCast(*const ZigClangNamedDecl, member_decl);
...@@ -3036,7 +3101,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar...@@ -3036,7 +3101,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
3036 else3101 else
3037 return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),3102 return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
3038 .AddrOf => {3103 .AddrOf => {
3039 const op_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");3104 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3040 op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);3105 op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value);
3041 return &op_node.base;3106 return &op_node.base;
3042 },3107 },
...@@ -3052,7 +3117,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar...@@ -3052,7 +3117,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
3052 .Plus => return transExpr(rp, scope, op_expr, used, .r_value),3117 .Plus => return transExpr(rp, scope, op_expr, used, .r_value),
3053 .Minus => {3118 .Minus => {
3054 if (!qualTypeHasWrappingOverflow(ZigClangExpr_getType(op_expr))) {3119 if (!qualTypeHasWrappingOverflow(ZigClangExpr_getType(op_expr))) {
3055 const op_node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-");3120 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .Negation, .Minus, "-");
3056 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);3121 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3057 return &op_node.base;3122 return &op_node.base;
3058 } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) {3123 } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) {
...@@ -3065,12 +3130,12 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar...@@ -3065,12 +3130,12 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar
3065 return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{});3130 return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{});
3066 },3131 },
3067 .Not => {3132 .Not => {
3068 const op_node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~");3133 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~");
3069 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);3134 op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3070 return &op_node.base;3135 return &op_node.base;
3071 },3136 },
3072 .LNot => {3137 .LNot => {
3073 const op_node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!");3138 const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!");
3074 op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);3139 op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true);
3075 return &op_node.base;3140 return &op_node.base;
3076 },3141 },
...@@ -3085,7 +3150,7 @@ fn transCreatePreCrement(...@@ -3085,7 +3150,7 @@ fn transCreatePreCrement(
3085 rp: RestorePoint,3150 rp: RestorePoint,
3086 scope: *Scope,3151 scope: *Scope,
3087 stmt: *const ZigClangUnaryOperator,3152 stmt: *const ZigClangUnaryOperator,
3088 op: ast.Node.InfixOp.Op,3153 op: ast.Node.Tag,
3089 op_tok_id: std.zig.Token.Id,3154 op_tok_id: std.zig.Token.Id,
3090 bytes: []const u8,3155 bytes: []const u8,
3091 used: ResultUsed,3156 used: ResultUsed,
...@@ -3114,12 +3179,21 @@ fn transCreatePreCrement(...@@ -3114,12 +3179,21 @@ fn transCreatePreCrement(
3114 defer block_scope.deinit();3179 defer block_scope.deinit();
3115 const ref = try block_scope.makeMangledName(rp.c, "ref");3180 const ref = try block_scope.makeMangledName(rp.c, "ref");
31163181
3117 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);3182 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3118 node.eq_token = try appendToken(rp.c, .Equal, "=");3183 const name_tok = try appendIdentifier(rp.c, ref);
3119 const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");3184 const eq_token = try appendToken(rp.c, .Equal, "=");
3185 const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3120 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);3186 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3121 node.init_node = &rhs_node.base;3187 const init_node = &rhs_node.base;
3122 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");3188 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3189 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
3190 .name_token = name_tok,
3191 .mut_token = mut_tok,
3192 .semicolon_token = semicolon_token,
3193 }, .{
3194 .eq_token = eq_token,
3195 .init_node = init_node,
3196 });
3123 try block_scope.statements.append(&node.base);3197 try block_scope.statements.append(&node.base);
31243198
3125 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);3199 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
...@@ -3150,7 +3224,7 @@ fn transCreatePostCrement(...@@ -3150,7 +3224,7 @@ fn transCreatePostCrement(
3150 rp: RestorePoint,3224 rp: RestorePoint,
3151 scope: *Scope,3225 scope: *Scope,
3152 stmt: *const ZigClangUnaryOperator,3226 stmt: *const ZigClangUnaryOperator,
3153 op: ast.Node.InfixOp.Op,3227 op: ast.Node.Tag,
3154 op_tok_id: std.zig.Token.Id,3228 op_tok_id: std.zig.Token.Id,
3155 bytes: []const u8,3229 bytes: []const u8,
3156 used: ResultUsed,3230 used: ResultUsed,
...@@ -3180,12 +3254,21 @@ fn transCreatePostCrement(...@@ -3180,12 +3254,21 @@ fn transCreatePostCrement(
3180 defer block_scope.deinit();3254 defer block_scope.deinit();
3181 const ref = try block_scope.makeMangledName(rp.c, "ref");3255 const ref = try block_scope.makeMangledName(rp.c, "ref");
31823256
3183 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);3257 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3184 node.eq_token = try appendToken(rp.c, .Equal, "=");3258 const name_tok = try appendIdentifier(rp.c, ref);
3185 const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");3259 const eq_token = try appendToken(rp.c, .Equal, "=");
3260 const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3186 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);3261 rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
3187 node.init_node = &rhs_node.base;3262 const init_node = &rhs_node.base;
3188 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");3263 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3264 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
3265 .name_token = name_tok,
3266 .mut_token = mut_tok,
3267 .semicolon_token = semicolon_token,
3268 }, .{
3269 .eq_token = eq_token,
3270 .init_node = init_node,
3271 });
3189 try block_scope.statements.append(&node.base);3272 try block_scope.statements.append(&node.base);
31903273
3191 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);3274 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
...@@ -3193,10 +3276,19 @@ fn transCreatePostCrement(...@@ -3193,10 +3276,19 @@ fn transCreatePostCrement(
3193 _ = try appendToken(rp.c, .Semicolon, ";");3276 _ = try appendToken(rp.c, .Semicolon, ";");
31943277
3195 const tmp = try block_scope.makeMangledName(rp.c, "tmp");3278 const tmp = try block_scope.makeMangledName(rp.c, "tmp");
3196 const tmp_node = try transCreateNodeVarDecl(rp.c, false, true, tmp);3279 const tmp_mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3197 tmp_node.eq_token = try appendToken(rp.c, .Equal, "=");3280 const tmp_name_tok = try appendIdentifier(rp.c, tmp);
3198 tmp_node.init_node = ref_node;3281 const tmp_eq_token = try appendToken(rp.c, .Equal, "=");
3199 tmp_node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");3282 const tmp_init_node = ref_node;
3283 const tmp_semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3284 const tmp_node = try ast.Node.VarDecl.create(rp.c.arena, .{
3285 .name_token = tmp_name_tok,
3286 .mut_token = tmp_mut_tok,
3287 .semicolon_token = semicolon_token,
3288 }, .{
3289 .eq_token = tmp_eq_token,
3290 .init_node = tmp_init_node,
3291 });
3200 try block_scope.statements.append(&tmp_node.base);3292 try block_scope.statements.append(&tmp_node.base);
32013293
3202 const token = try appendToken(rp.c, op_tok_id, bytes);3294 const token = try appendToken(rp.c, op_tok_id, bytes);
...@@ -3254,10 +3346,10 @@ fn transCreateCompoundAssign(...@@ -3254,10 +3346,10 @@ fn transCreateCompoundAssign(
3254 rp: RestorePoint,3346 rp: RestorePoint,
3255 scope: *Scope,3347 scope: *Scope,
3256 stmt: *const ZigClangCompoundAssignOperator,3348 stmt: *const ZigClangCompoundAssignOperator,
3257 assign_op: ast.Node.InfixOp.Op,3349 assign_op: ast.Node.Tag,
3258 assign_tok_id: std.zig.Token.Id,3350 assign_tok_id: std.zig.Token.Id,
3259 assign_bytes: []const u8,3351 assign_bytes: []const u8,
3260 bin_op: ast.Node.InfixOp.Op,3352 bin_op: ast.Node.Tag,
3261 bin_tok_id: std.zig.Token.Id,3353 bin_tok_id: std.zig.Token.Id,
3262 bin_bytes: []const u8,3354 bin_bytes: []const u8,
3263 used: ResultUsed,3355 used: ResultUsed,
...@@ -3268,14 +3360,21 @@ fn transCreateCompoundAssign(...@@ -3268,14 +3360,21 @@ fn transCreateCompoundAssign(
3268 const lhs = ZigClangCompoundAssignOperator_getLHS(stmt);3360 const lhs = ZigClangCompoundAssignOperator_getLHS(stmt);
3269 const rhs = ZigClangCompoundAssignOperator_getRHS(stmt);3361 const rhs = ZigClangCompoundAssignOperator_getRHS(stmt);
3270 const loc = ZigClangCompoundAssignOperator_getBeginLoc(stmt);3362 const loc = ZigClangCompoundAssignOperator_getBeginLoc(stmt);
3271 const is_signed = cIsSignedInteger(getExprQualType(rp.c, lhs));3363 const lhs_qt = getExprQualType(rp.c, lhs);
3364 const rhs_qt = getExprQualType(rp.c, rhs);
3365 const is_signed = cIsSignedInteger(lhs_qt);
3366 const requires_int_cast = blk: {
3367 const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);
3368 const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt);
3369 break :blk are_integers and !are_same_sign;
3370 };
3272 if (used == .unused) {3371 if (used == .unused) {
3273 // common case3372 // common case
3274 // c: lhs += rhs3373 // c: lhs += rhs
3275 // zig: lhs += rhs3374 // zig: lhs += rhs
3276 if ((is_mod or is_div) and is_signed) {3375 if ((is_mod or is_div) and is_signed) {
3277 const op_token = try appendToken(rp.c, .Equal, "=");3376 const op_token = try appendToken(rp.c, .Equal, "=");
3278 const op_node = try rp.c.arena.create(ast.Node.InfixOp);3377 const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
3279 const builtin = if (is_mod) "@rem" else "@divTrunc";3378 const builtin = if (is_mod) "@rem" else "@divTrunc";
3280 const builtin_node = try rp.c.createBuiltinCall(builtin, 2);3379 const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
3281 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);3380 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
...@@ -3284,9 +3383,9 @@ fn transCreateCompoundAssign(...@@ -3284,9 +3383,9 @@ fn transCreateCompoundAssign(
3284 builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);3383 builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);
3285 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");3384 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3286 op_node.* = .{3385 op_node.* = .{
3386 .base = .{ .tag = .Assign },
3287 .op_token = op_token,3387 .op_token = op_token,
3288 .lhs = lhs_node,3388 .lhs = lhs_node,
3289 .op = .Assign,
3290 .rhs = &builtin_node.base,3389 .rhs = &builtin_node.base,
3291 };3390 };
3292 _ = try appendToken(rp.c, .Semicolon, ";");3391 _ = try appendToken(rp.c, .Semicolon, ";");
...@@ -3295,15 +3394,18 @@ fn transCreateCompoundAssign(...@@ -3295,15 +3394,18 @@ fn transCreateCompoundAssign(
32953394
3296 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);3395 const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
3297 const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes);3396 const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes);
3298 var rhs_node = if (is_shift)3397 var rhs_node = if (is_shift or requires_int_cast)
3299 try transExprCoercing(rp, scope, rhs, .used, .r_value)3398 try transExprCoercing(rp, scope, rhs, .used, .r_value)
3300 else3399 else
3301 try transExpr(rp, scope, rhs, .used, .r_value);3400 try transExpr(rp, scope, rhs, .used, .r_value);
33023401
3303 if (is_shift) {3402 if (is_shift or requires_int_cast) {
3304 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);3403 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
3305 const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc);3404 const cast_to_type = if (is_shift)
3306 cast_node.params()[0] = rhs_type;3405 try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc)
3406 else
3407 try transQualType(rp, getExprQualType(rp.c, lhs), loc);
3408 cast_node.params()[0] = cast_to_type;
3307 _ = try appendToken(rp.c, .Comma, ",");3409 _ = try appendToken(rp.c, .Comma, ",");
3308 cast_node.params()[1] = rhs_node;3410 cast_node.params()[1] = rhs_node;
3309 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");3411 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
...@@ -3324,12 +3426,21 @@ fn transCreateCompoundAssign(...@@ -3324,12 +3426,21 @@ fn transCreateCompoundAssign(
3324 defer block_scope.deinit();3426 defer block_scope.deinit();
3325 const ref = try block_scope.makeMangledName(rp.c, "ref");3427 const ref = try block_scope.makeMangledName(rp.c, "ref");
33263428
3327 const node = try transCreateNodeVarDecl(rp.c, false, true, ref);3429 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3328 node.eq_token = try appendToken(rp.c, .Equal, "=");3430 const name_tok = try appendIdentifier(rp.c, ref);
3329 const addr_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&");3431 const eq_token = try appendToken(rp.c, .Equal, "=");
3432 const addr_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
3330 addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value);3433 addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value);
3331 node.init_node = &addr_node.base;3434 const init_node = &addr_node.base;
3332 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");3435 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3436 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
3437 .name_token = name_tok,
3438 .mut_token = mut_tok,
3439 .semicolon_token = semicolon_token,
3440 }, .{
3441 .eq_token = eq_token,
3442 .init_node = init_node,
3443 });
3333 try block_scope.statements.append(&node.base);3444 try block_scope.statements.append(&node.base);
33343445
3335 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);3446 const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
...@@ -3338,7 +3449,7 @@ fn transCreateCompoundAssign(...@@ -3338,7 +3449,7 @@ fn transCreateCompoundAssign(
33383449
3339 if ((is_mod or is_div) and is_signed) {3450 if ((is_mod or is_div) and is_signed) {
3340 const op_token = try appendToken(rp.c, .Equal, "=");3451 const op_token = try appendToken(rp.c, .Equal, "=");
3341 const op_node = try rp.c.arena.create(ast.Node.InfixOp);3452 const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
3342 const builtin = if (is_mod) "@rem" else "@divTrunc";3453 const builtin = if (is_mod) "@rem" else "@divTrunc";
3343 const builtin_node = try rp.c.createBuiltinCall(builtin, 2);3454 const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
3344 builtin_node.params()[0] = try transCreateNodePtrDeref(rp.c, lhs_node);3455 builtin_node.params()[0] = try transCreateNodePtrDeref(rp.c, lhs_node);
...@@ -3347,9 +3458,9 @@ fn transCreateCompoundAssign(...@@ -3347,9 +3458,9 @@ fn transCreateCompoundAssign(
3347 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");3458 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3348 _ = try appendToken(rp.c, .Semicolon, ";");3459 _ = try appendToken(rp.c, .Semicolon, ";");
3349 op_node.* = .{3460 op_node.* = .{
3461 .base = .{ .tag = .Assign },
3350 .op_token = op_token,3462 .op_token = op_token,
3351 .lhs = ref_node,3463 .lhs = ref_node,
3352 .op = .Assign,
3353 .rhs = &builtin_node.base,3464 .rhs = &builtin_node.base,
3354 };3465 };
3355 _ = try appendToken(rp.c, .Semicolon, ";");3466 _ = try appendToken(rp.c, .Semicolon, ";");
...@@ -3358,10 +3469,13 @@ fn transCreateCompoundAssign(...@@ -3358,10 +3469,13 @@ fn transCreateCompoundAssign(
3358 const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes);3469 const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes);
3359 var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);3470 var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
33603471
3361 if (is_shift) {3472 if (is_shift or requires_int_cast) {
3362 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);3473 const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
3363 const rhs_type = try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc);3474 const cast_to_type = if (is_shift)
3364 cast_node.params()[0] = rhs_type;3475 try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc)
3476 else
3477 try transQualType(rp, getExprQualType(rp.c, lhs), loc);
3478 cast_node.params()[0] = cast_to_type;
3365 _ = try appendToken(rp.c, .Comma, ",");3479 _ = try appendToken(rp.c, .Comma, ",");
3366 cast_node.params()[1] = rhs_node;3480 cast_node.params()[1] = rhs_node;
3367 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");3481 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
...@@ -3371,8 +3485,8 @@ fn transCreateCompoundAssign(...@@ -3371,8 +3485,8 @@ fn transCreateCompoundAssign(
3371 const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false);3485 const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false);
3372 _ = try appendToken(rp.c, .Semicolon, ";");3486 _ = try appendToken(rp.c, .Semicolon, ";");
33733487
3374 const eq_token = try appendToken(rp.c, .Equal, "=");3488 const ass_eq_token = try appendToken(rp.c, .Equal, "=");
3375 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, eq_token, rhs_bin, .used, false);3489 const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, ass_eq_token, rhs_bin, .used, false);
3376 try block_scope.statements.append(assign);3490 try block_scope.statements.append(assign);
3377 }3491 }
33783492
...@@ -3490,10 +3604,19 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const...@@ -3490,10 +3604,19 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const
3490 defer block_scope.deinit();3604 defer block_scope.deinit();
34913605
3492 const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp");3606 const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp");
3493 const tmp_var = try transCreateNodeVarDecl(rp.c, false, true, mangled_name);3607 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3494 tmp_var.eq_token = try appendToken(rp.c, .Equal, "=");3608 const name_tok = try appendIdentifier(rp.c, mangled_name);
3495 tmp_var.init_node = try transExpr(rp, &block_scope.base, cond_expr, .used, .r_value);3609 const eq_token = try appendToken(rp.c, .Equal, "=");
3496 tmp_var.semicolon_token = try appendToken(rp.c, .Semicolon, ";");3610 const init_node = try transExpr(rp, &block_scope.base, cond_expr, .used, .r_value);
3611 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
3612 const tmp_var = try ast.Node.VarDecl.create(rp.c.arena, .{
3613 .name_token = name_tok,
3614 .mut_token = mut_tok,
3615 .semicolon_token = semicolon_token,
3616 }, .{
3617 .eq_token = eq_token,
3618 .init_node = init_node,
3619 });
3497 try block_scope.statements.append(&tmp_var.base);3620 try block_scope.statements.append(&tmp_var.base);
34983621
3499 const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label);3622 const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label);
...@@ -3590,11 +3713,11 @@ fn maybeSuppressResult(...@@ -3590,11 +3713,11 @@ fn maybeSuppressResult(
3590 }3713 }
3591 const lhs = try transCreateNodeIdentifier(rp.c, "_");3714 const lhs = try transCreateNodeIdentifier(rp.c, "_");
3592 const op_token = try appendToken(rp.c, .Equal, "=");3715 const op_token = try appendToken(rp.c, .Equal, "=");
3593 const op_node = try rp.c.arena.create(ast.Node.InfixOp);3716 const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
3594 op_node.* = .{3717 op_node.* = .{
3718 .base = .{ .tag = .Assign },
3595 .op_token = op_token,3719 .op_token = op_token,
3596 .lhs = lhs,3720 .lhs = lhs,
3597 .op = .Assign,
3598 .rhs = result,3721 .rhs = result,
3599 };3722 };
3600 return &op_node.base;3723 return &op_node.base;
...@@ -3928,9 +4051,9 @@ fn transCreateNodeAssign(...@@ -3928,9 +4051,9 @@ fn transCreateNodeAssign(
3928 defer block_scope.deinit();4051 defer block_scope.deinit();
39294052
3930 const tmp = try block_scope.makeMangledName(rp.c, "tmp");4053 const tmp = try block_scope.makeMangledName(rp.c, "tmp");
39314054 const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
3932 const node = try transCreateNodeVarDecl(rp.c, false, true, tmp);4055 const name_tok = try appendIdentifier(rp.c, tmp);
3933 node.eq_token = try appendToken(rp.c, .Equal, "=");4056 const eq_token = try appendToken(rp.c, .Equal, "=");
3934 var rhs_node = try transExpr(rp, &block_scope.base, rhs, .used, .r_value);4057 var rhs_node = try transExpr(rp, &block_scope.base, rhs, .used, .r_value);
3935 if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {4058 if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
3936 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);4059 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
...@@ -3938,16 +4061,24 @@ fn transCreateNodeAssign(...@@ -3938,16 +4061,24 @@ fn transCreateNodeAssign(
3938 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");4061 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3939 rhs_node = &builtin_node.base;4062 rhs_node = &builtin_node.base;
3940 }4063 }
3941 node.init_node = rhs_node;4064 const init_node = rhs_node;
3942 node.semicolon_token = try appendToken(rp.c, .Semicolon, ";");4065 const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
4066 const node = try ast.Node.VarDecl.create(rp.c.arena, .{
4067 .name_token = name_tok,
4068 .mut_token = mut_tok,
4069 .semicolon_token = semicolon_token,
4070 }, .{
4071 .eq_token = eq_token,
4072 .init_node = init_node,
4073 });
3943 try block_scope.statements.append(&node.base);4074 try block_scope.statements.append(&node.base);
39444075
3945 const lhs_node = try transExpr(rp, &block_scope.base, lhs, .used, .l_value);4076 const lhs_node = try transExpr(rp, &block_scope.base, lhs, .used, .l_value);
3946 const eq_token = try appendToken(rp.c, .Equal, "=");4077 const lhs_eq_token = try appendToken(rp.c, .Equal, "=");
3947 const ident = try transCreateNodeIdentifier(rp.c, tmp);4078 const ident = try transCreateNodeIdentifier(rp.c, tmp);
3948 _ = try appendToken(rp.c, .Semicolon, ";");4079 _ = try appendToken(rp.c, .Semicolon, ";");
39494080
3950 const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, eq_token, ident, .used, false);4081 const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, lhs_eq_token, ident, .used, false);
3951 try block_scope.statements.append(assign);4082 try block_scope.statements.append(assign);
39524083
3953 const break_node = try transCreateNodeBreak(rp.c, label_name);4084 const break_node = try transCreateNodeBreak(rp.c, label_name);
...@@ -3961,26 +4092,26 @@ fn transCreateNodeAssign(...@@ -3961,26 +4092,26 @@ fn transCreateNodeAssign(
3961}4092}
39624093
3963fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {4094fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {
3964 const field_access_node = try c.arena.create(ast.Node.InfixOp);4095 const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
3965 field_access_node.* = .{4096 field_access_node.* = .{
4097 .base = .{ .tag = .Period },
3966 .op_token = try appendToken(c, .Period, "."),4098 .op_token = try appendToken(c, .Period, "."),
3967 .lhs = container,4099 .lhs = container,
3968 .op = .Period,
3969 .rhs = try transCreateNodeIdentifier(c, field_name),4100 .rhs = try transCreateNodeIdentifier(c, field_name),
3970 };4101 };
3971 return &field_access_node.base;4102 return &field_access_node.base;
3972}4103}
39734104
3974fn transCreateNodePrefixOp(4105fn transCreateNodeSimplePrefixOp(
3975 c: *Context,4106 c: *Context,
3976 op: ast.Node.PrefixOp.Op,4107 comptime tag: ast.Node.Tag,
3977 op_tok_id: std.zig.Token.Id,4108 op_tok_id: std.zig.Token.Id,
3978 bytes: []const u8,4109 bytes: []const u8,
3979) !*ast.Node.PrefixOp {4110) !*ast.Node.SimplePrefixOp {
3980 const node = try c.arena.create(ast.Node.PrefixOp);4111 const node = try c.arena.create(ast.Node.SimplePrefixOp);
3981 node.* = .{4112 node.* = .{
4113 .base = .{ .tag = tag },
3982 .op_token = try appendToken(c, op_tok_id, bytes),4114 .op_token = try appendToken(c, op_tok_id, bytes),
3983 .op = op,
3984 .rhs = undefined, // translate and set afterward4115 .rhs = undefined, // translate and set afterward
3985 };4116 };
3986 return node;4117 return node;
...@@ -3990,7 +4121,7 @@ fn transCreateNodeInfixOp(...@@ -3990,7 +4121,7 @@ fn transCreateNodeInfixOp(
3990 rp: RestorePoint,4121 rp: RestorePoint,
3991 scope: *Scope,4122 scope: *Scope,
3992 lhs_node: *ast.Node,4123 lhs_node: *ast.Node,
3993 op: ast.Node.InfixOp.Op,4124 op: ast.Node.Tag,
3994 op_token: ast.TokenIndex,4125 op_token: ast.TokenIndex,
3995 rhs_node: *ast.Node,4126 rhs_node: *ast.Node,
3996 used: ResultUsed,4127 used: ResultUsed,
...@@ -4000,11 +4131,11 @@ fn transCreateNodeInfixOp(...@@ -4000,11 +4131,11 @@ fn transCreateNodeInfixOp(
4000 try appendToken(rp.c, .LParen, "(")4131 try appendToken(rp.c, .LParen, "(")
4001 else4132 else
4002 null;4133 null;
4003 const node = try rp.c.arena.create(ast.Node.InfixOp);4134 const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
4004 node.* = .{4135 node.* = .{
4136 .base = .{ .tag = op },
4005 .op_token = op_token,4137 .op_token = op_token,
4006 .lhs = lhs_node,4138 .lhs = lhs_node,
4007 .op = op,
4008 .rhs = rhs_node,4139 .rhs = rhs_node,
4009 };4140 };
4010 if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base);4141 if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base);
...@@ -4022,7 +4153,7 @@ fn transCreateNodeBoolInfixOp(...@@ -4022,7 +4153,7 @@ fn transCreateNodeBoolInfixOp(
4022 rp: RestorePoint,4153 rp: RestorePoint,
4023 scope: *Scope,4154 scope: *Scope,
4024 stmt: *const ZigClangBinaryOperator,4155 stmt: *const ZigClangBinaryOperator,
4025 op: ast.Node.InfixOp.Op,4156 op: ast.Node.Tag,
4026 used: ResultUsed,4157 used: ResultUsed,
4027 grouped: bool,4158 grouped: bool,
4028) !*ast.Node {4159) !*ast.Node {
...@@ -4052,8 +4183,8 @@ fn transCreateNodePtrType(...@@ -4052,8 +4183,8 @@ fn transCreateNodePtrType(
4052 is_const: bool,4183 is_const: bool,
4053 is_volatile: bool,4184 is_volatile: bool,
4054 op_tok_id: std.zig.Token.Id,4185 op_tok_id: std.zig.Token.Id,
4055) !*ast.Node.PrefixOp {4186) !*ast.Node.PtrType {
4056 const node = try c.arena.create(ast.Node.PrefixOp);4187 const node = try c.arena.create(ast.Node.PtrType);
4057 const op_token = switch (op_tok_id) {4188 const op_token = switch (op_tok_id) {
4058 .LBracket => blk: {4189 .LBracket => blk: {
4059 const lbracket = try appendToken(c, .LBracket, "[");4190 const lbracket = try appendToken(c, .LBracket, "[");
...@@ -4073,11 +4204,9 @@ fn transCreateNodePtrType(...@@ -4073,11 +4204,9 @@ fn transCreateNodePtrType(
4073 };4204 };
4074 node.* = .{4205 node.* = .{
4075 .op_token = op_token,4206 .op_token = op_token,
4076 .op = .{4207 .ptr_info = .{
4077 .PtrType = .{4208 .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,
4078 .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null,4209 .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
4079 .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null,
4080 },
4081 },4210 },
4082 .rhs = undefined, // translate and set afterward4211 .rhs = undefined, // translate and set afterward
4083 };4212 };
...@@ -4174,7 +4303,7 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {...@@ -4174,7 +4303,7 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {
4174 return &node.base;4303 return &node.base;
4175}4304}
41764305
4177fn transCreateNodeInt(c: *Context, int: var) !*ast.Node {4306fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node {
4178 const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int});4307 const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int});
4179 const node = try c.arena.create(ast.Node.IntegerLiteral);4308 const node = try c.arena.create(ast.Node.IntegerLiteral);
4180 node.* = .{4309 node.* = .{
...@@ -4183,7 +4312,7 @@ fn transCreateNodeInt(c: *Context, int: var) !*ast.Node {...@@ -4183,7 +4312,7 @@ fn transCreateNodeInt(c: *Context, int: var) !*ast.Node {
4183 return &node.base;4312 return &node.base;
4184}4313}
41854314
4186fn transCreateNodeFloat(c: *Context, int: var) !*ast.Node {4315fn transCreateNodeFloat(c: *Context, int: anytype) !*ast.Node {
4187 const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int});4316 const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int});
4188 const node = try c.arena.create(ast.Node.FloatLiteral);4317 const node = try c.arena.create(ast.Node.FloatLiteral);
4189 node.* = .{4318 node.* = .{
...@@ -4231,28 +4360,10 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a...@@ -4231,28 +4360,10 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
42314360
4232 _ = try appendToken(c, .RParen, ")");4361 _ = try appendToken(c, .RParen, ")");
42334362
4234 const fn_proto = try ast.Node.FnProto.alloc(c.arena, fn_params.items.len);
4235 fn_proto.* = .{
4236 .doc_comments = null,
4237 .visib_token = pub_tok,
4238 .fn_token = fn_tok,
4239 .name_token = name_tok,
4240 .params_len = fn_params.items.len,
4241 .return_type = proto_alias.return_type,
4242 .var_args_token = null,
4243 .extern_export_inline_token = inline_tok,
4244 .body_node = null,
4245 .lib_name = null,
4246 .align_expr = null,
4247 .section_expr = null,
4248 .callconv_expr = null,
4249 };
4250 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
4251
4252 const block_lbrace = try appendToken(c, .LBrace, "{");4363 const block_lbrace = try appendToken(c, .LBrace, "{");
42534364
4254 const return_expr = try transCreateNodeReturnExpr(c);4365 const return_expr = try transCreateNodeReturnExpr(c);
4255 const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.init_node.?);4366 const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.getTrailer("init_node").?);
42564367
4257 const call_expr = try c.createCall(unwrap_expr, fn_params.items.len);4368 const call_expr = try c.createCall(unwrap_expr, fn_params.items.len);
4258 const call_params = call_expr.params();4369 const call_params = call_expr.params();
...@@ -4276,7 +4387,18 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a...@@ -4276,7 +4387,18 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
4276 .rbrace = try appendToken(c, .RBrace, "}"),4387 .rbrace = try appendToken(c, .RBrace, "}"),
4277 };4388 };
4278 block.statements()[0] = &return_expr.base;4389 block.statements()[0] = &return_expr.base;
4279 fn_proto.body_node = &block.base;4390
4391 const fn_proto = try ast.Node.FnProto.create(c.arena, .{
4392 .params_len = fn_params.items.len,
4393 .fn_token = fn_tok,
4394 .return_type = proto_alias.return_type,
4395 }, .{
4396 .visib_token = pub_tok,
4397 .name_token = name_tok,
4398 .extern_export_inline_token = inline_tok,
4399 .body_node = &block.base,
4400 });
4401 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
4280 return &fn_proto.base;4402 return &fn_proto.base;
4281}4403}
42824404
...@@ -4355,31 +4477,6 @@ fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowE...@@ -4355,31 +4477,6 @@ fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowE
4355 return node;4477 return node;
4356}4478}
43574479
4358fn transCreateNodeVarDecl(c: *Context, is_pub: bool, is_const: bool, name: []const u8) !*ast.Node.VarDecl {
4359 const visib_tok = if (is_pub) try appendToken(c, .Keyword_pub, "pub") else null;
4360 const mut_tok = if (is_const) try appendToken(c, .Keyword_const, "const") else try appendToken(c, .Keyword_var, "var");
4361 const name_tok = try appendIdentifier(c, name);
4362
4363 const node = try c.arena.create(ast.Node.VarDecl);
4364 node.* = .{
4365 .doc_comments = null,
4366 .visib_token = visib_tok,
4367 .thread_local_token = null,
4368 .name_token = name_tok,
4369 .eq_token = undefined,
4370 .mut_token = mut_tok,
4371 .comptime_token = null,
4372 .extern_export_token = null,
4373 .lib_name = null,
4374 .type_node = null,
4375 .align_node = null,
4376 .section_node = null,
4377 .init_node = null,
4378 .semicolon_token = undefined,
4379 };
4380 return node;
4381}
4382
4383fn transCreateNodeWhile(c: *Context) !*ast.Node.While {4480fn transCreateNodeWhile(c: *Context) !*ast.Node.While {
4384 const while_tok = try appendToken(c, .Keyword_while, "while");4481 const while_tok = try appendToken(c, .Keyword_while, "while");
4385 _ = try appendToken(c, .LParen, "(");4482 _ = try appendToken(c, .LParen, "(");
...@@ -4436,7 +4533,7 @@ fn transCreateNodeShiftOp(...@@ -4436,7 +4533,7 @@ fn transCreateNodeShiftOp(
4436 rp: RestorePoint,4533 rp: RestorePoint,
4437 scope: *Scope,4534 scope: *Scope,
4438 stmt: *const ZigClangBinaryOperator,4535 stmt: *const ZigClangBinaryOperator,
4439 op: ast.Node.InfixOp.Op,4536 op: ast.Node.Tag,
4440 op_tok_id: std.zig.Token.Id,4537 op_tok_id: std.zig.Token.Id,
4441 bytes: []const u8,4538 bytes: []const u8,
4442) !*ast.Node {4539) !*ast.Node {
...@@ -4458,11 +4555,11 @@ fn transCreateNodeShiftOp(...@@ -4458,11 +4555,11 @@ fn transCreateNodeShiftOp(
4458 cast_node.params()[1] = rhs;4555 cast_node.params()[1] = rhs;
4459 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");4556 cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
44604557
4461 const node = try rp.c.arena.create(ast.Node.InfixOp);4558 const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
4462 node.* = .{4559 node.* = .{
4560 .base = .{ .tag = op },
4463 .op_token = op_token,4561 .op_token = op_token,
4464 .lhs = lhs,4562 .lhs = lhs,
4465 .op = op,
4466 .rhs = &cast_node.base,4563 .rhs = &cast_node.base,
4467 };4564 };
44684565
...@@ -4556,12 +4653,12 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour...@@ -4556,12 +4653,12 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
4556 .Pointer => {4653 .Pointer => {
4557 const child_qt = ZigClangType_getPointeeType(ty);4654 const child_qt = ZigClangType_getPointeeType(ty);
4558 if (qualTypeChildIsFnProto(child_qt)) {4655 if (qualTypeChildIsFnProto(child_qt)) {
4559 const optional_node = try transCreateNodePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");4656 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
4560 optional_node.rhs = try transQualType(rp, child_qt, source_loc);4657 optional_node.rhs = try transQualType(rp, child_qt, source_loc);
4561 return &optional_node.base;4658 return &optional_node.base;
4562 }4659 }
4563 if (typeIsOpaque(rp.c, ZigClangQualType_getTypePtr(child_qt), source_loc)) {4660 if (typeIsOpaque(rp.c, ZigClangQualType_getTypePtr(child_qt), source_loc)) {
4564 const optional_node = try transCreateNodePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");4661 const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?");
4565 const pointer_node = try transCreateNodePtrType(4662 const pointer_node = try transCreateNodePtrType(
4566 rp.c,4663 rp.c,
4567 ZigClangQualType_isConstQualified(child_qt),4664 ZigClangQualType_isConstQualified(child_qt),
...@@ -4586,21 +4683,8 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour...@@ -4586,21 +4683,8 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour
45864683
4587 const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);4684 const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty);
4588 const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));4685 const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize));
4589 var node = try transCreateNodePrefixOp(4686 const elem_ty = ZigClangQualType_getTypePtr(ZigClangConstantArrayType_getElementType(const_arr_ty));
4590 rp.c,4687 return try transCreateNodeArrayType(rp, source_loc, elem_ty, size);
4591 .{
4592 .ArrayType = .{
4593 .len_expr = undefined,
4594 .sentinel = null,
4595 },
4596 },
4597 .LBracket,
4598 "[",
4599 );
4600 node.op.ArrayType.len_expr = try transCreateNodeInt(rp.c, size);
4601 _ = try appendToken(rp.c, .RBracket, "]");
4602 node.rhs = try transQualType(rp, ZigClangConstantArrayType_getElementType(const_arr_ty), source_loc);
4603 return &node.base;
4604 },4688 },
4605 .IncompleteArray => {4689 .IncompleteArray => {
4606 const incomplete_array_ty = @ptrCast(*const ZigClangIncompleteArrayType, ty);4690 const incomplete_array_ty = @ptrCast(*const ZigClangIncompleteArrayType, ty);
...@@ -4794,19 +4878,12 @@ fn finishTransFnProto(...@@ -4794,19 +4878,12 @@ fn finishTransFnProto(
4794 }4878 }
4795 }4879 }
47964880
4797 if (is_var_args) {4881 const var_args_token: ?ast.TokenIndex = if (is_var_args) blk: {
4798 if (param_count > 0) {4882 if (param_count > 0) {
4799 _ = try appendToken(rp.c, .Comma, ",");4883 _ = try appendToken(rp.c, .Comma, ",");
4800 }4884 }
48014885 break :blk try appendToken(rp.c, .Ellipsis3, "...");
4802 fn_params.addOneAssumeCapacity().* = .{4886 } else null;
4803 .doc_comments = null,
4804 .comptime_token = null,
4805 .noalias_token = null,
4806 .name_token = null,
4807 .param_type = .{ .var_args = try appendToken(rp.c, .Ellipsis3, "...") },
4808 };
4809 }
48104887
4811 const rparen_tok = try appendToken(rp.c, .RParen, ")");4888 const rparen_tok = try appendToken(rp.c, .RParen, ")");
48124889
...@@ -4872,44 +4949,53 @@ fn finishTransFnProto(...@@ -4872,44 +4949,53 @@ fn finishTransFnProto(
4872 }4949 }
4873 };4950 };
48744951
4875 const fn_proto = try ast.Node.FnProto.alloc(rp.c.arena, fn_params.items.len);4952 // We need to reserve an undefined (but non-null) body node to set later.
4876 fn_proto.* = .{4953 var body_node: ?*ast.Node = null;
4877 .doc_comments = null,4954 if (fn_decl_context) |ctx| {
4878 .visib_token = pub_tok,4955 if (ctx.has_body) {
4879 .fn_token = fn_tok,4956 // TODO: we should be able to use undefined here but
4880 .name_token = name_tok,4957 // it causes a bug. This is undefined without zig language
4958 // being aware of it.
4959 body_node = @intToPtr(*ast.Node, 0x08);
4960 }
4961 }
4962
4963 const fn_proto = try ast.Node.FnProto.create(rp.c.arena, .{
4881 .params_len = fn_params.items.len,4964 .params_len = fn_params.items.len,
4882 .return_type = .{ .Explicit = return_type_node },4965 .return_type = .{ .Explicit = return_type_node },
4883 .var_args_token = null, // TODO this field is broken in the AST data model4966 .fn_token = fn_tok,
4967 }, .{
4968 .visib_token = pub_tok,
4969 .name_token = name_tok,
4884 .extern_export_inline_token = extern_export_inline_tok,4970 .extern_export_inline_token = extern_export_inline_tok,
4885 .body_node = null,
4886 .lib_name = null,
4887 .align_expr = align_expr,4971 .align_expr = align_expr,
4888 .section_expr = linksection_expr,4972 .section_expr = linksection_expr,
4889 .callconv_expr = callconv_expr,4973 .callconv_expr = callconv_expr,
4890 };4974 .body_node = body_node,
4975 .var_args_token = var_args_token,
4976 });
4891 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);4977 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
4892 return fn_proto;4978 return fn_proto;
4893}4979}
48944980
4895fn revertAndWarn(4981fn revertAndWarn(
4896 rp: RestorePoint,4982 rp: RestorePoint,
4897 err: var,4983 err: anytype,
4898 source_loc: ZigClangSourceLocation,4984 source_loc: ZigClangSourceLocation,
4899 comptime format: []const u8,4985 comptime format: []const u8,
4900 args: var,4986 args: anytype,
4901) (@TypeOf(err) || error{OutOfMemory}) {4987) (@TypeOf(err) || error{OutOfMemory}) {
4902 rp.activate();4988 rp.activate();
4903 try emitWarning(rp.c, source_loc, format, args);4989 try emitWarning(rp.c, source_loc, format, args);
4904 return err;4990 return err;
4905}4991}
49064992
4907fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []const u8, args: var) !void {4993fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []const u8, args: anytype) !void {
4908 const args_prefix = .{c.locStr(loc)};4994 const args_prefix = .{c.locStr(loc)};
4909 _ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, args_prefix ++ args);4995 _ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, args_prefix ++ args);
4910}4996}
49114997
4912pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: var) !void {4998pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void {
4913 // pub const name = @compileError(msg);4999 // pub const name = @compileError(msg);
4914 const pub_tok = try appendToken(c, .Keyword_pub, "pub");5000 const pub_tok = try appendToken(c, .Keyword_pub, "pub");
4915 const const_tok = try appendToken(c, .Keyword_const, "const");5001 const const_tok = try appendToken(c, .Keyword_const, "const");
...@@ -4935,23 +5021,15 @@ pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comp...@@ -4935,23 +5021,15 @@ pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comp
4935 };5021 };
4936 call_node.params()[0] = &msg_node.base;5022 call_node.params()[0] = &msg_node.base;
49375023
4938 const var_decl_node = try c.arena.create(ast.Node.VarDecl);5024 const var_decl_node = try ast.Node.VarDecl.create(c.arena, .{
4939 var_decl_node.* = .{
4940 .doc_comments = null,
4941 .visib_token = pub_tok,
4942 .thread_local_token = null,
4943 .name_token = name_tok,5025 .name_token = name_tok,
4944 .eq_token = eq_tok,
4945 .mut_token = const_tok,5026 .mut_token = const_tok,
4946 .comptime_token = null,
4947 .extern_export_token = null,
4948 .lib_name = null,
4949 .type_node = null,
4950 .align_node = null,
4951 .section_node = null,
4952 .init_node = &call_node.base,
4953 .semicolon_token = semi_tok,5027 .semicolon_token = semi_tok,
4954 };5028 }, .{
5029 .visib_token = pub_tok,
5030 .eq_token = eq_tok,
5031 .init_node = &call_node.base,
5032 });
4955 try addTopLevelDecl(c, name, &var_decl_node.base);5033 try addTopLevelDecl(c, name, &var_decl_node.base);
4956}5034}
49575035
...@@ -4960,7 +5038,7 @@ fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenInd...@@ -4960,7 +5038,7 @@ fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenInd
4960 return appendTokenFmt(c, token_id, "{}", .{bytes});5038 return appendTokenFmt(c, token_id, "{}", .{bytes});
4961}5039}
49625040
4963fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: var) !ast.TokenIndex {5041fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex {
4964 assert(token_id != .Invalid);5042 assert(token_id != .Invalid);
49655043
4966 try c.token_ids.ensureCapacity(c.gpa, c.token_ids.items.len + 1);5044 try c.token_ids.ensureCapacity(c.gpa, c.token_ids.items.len + 1);
...@@ -5144,10 +5222,12 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {...@@ -5144,10 +5222,12 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
5144fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {5222fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
5145 const scope = &c.global_scope.base;5223 const scope = &c.global_scope.base;
51465224
5147 const node = try transCreateNodeVarDecl(c, true, true, name);5225 const visib_tok = try appendToken(c, .Keyword_pub, "pub");
5148 node.eq_token = try appendToken(c, .Equal, "=");5226 const mut_tok = try appendToken(c, .Keyword_const, "const");
5227 const name_tok = try appendIdentifier(c, name);
5228 const eq_token = try appendToken(c, .Equal, "=");
51495229
5150 node.init_node = try parseCExpr(c, it, source, source_loc, scope);5230 const init_node = try parseCExpr(c, it, source, source_loc, scope);
5151 const last = it.next().?;5231 const last = it.next().?;
5152 if (last.id != .Eof and last.id != .Nl)5232 if (last.id != .Eof and last.id != .Nl)
5153 return failDecl(5233 return failDecl(
...@@ -5158,7 +5238,16 @@ fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, n...@@ -5158,7 +5238,16 @@ fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, n
5158 .{@tagName(last.id)},5238 .{@tagName(last.id)},
5159 );5239 );
51605240
5161 node.semicolon_token = try appendToken(c, .Semicolon, ";");5241 const semicolon_token = try appendToken(c, .Semicolon, ";");
5242 const node = try ast.Node.VarDecl.create(c.arena, .{
5243 .name_token = name_tok,
5244 .mut_token = mut_tok,
5245 .semicolon_token = semicolon_token,
5246 }, .{
5247 .visib_token = visib_tok,
5248 .eq_token = eq_token,
5249 .init_node = init_node,
5250 });
5162 _ = try c.global_scope.macro_table.put(name, &node.base);5251 _ = try c.global_scope.macro_table.put(name, &node.base);
5163}5252}
51645253
...@@ -5202,10 +5291,9 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5202,10 +5291,9 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5202 const param_name_tok = try appendIdentifier(c, mangled_name);5291 const param_name_tok = try appendIdentifier(c, mangled_name);
5203 _ = try appendToken(c, .Colon, ":");5292 _ = try appendToken(c, .Colon, ":");
52045293
5205 const token_index = try appendToken(c, .Keyword_var, "var");5294 const any_type = try c.arena.create(ast.Node.AnyType);
5206 const identifier = try c.arena.create(ast.Node.Identifier);5295 any_type.* = .{
5207 identifier.* = .{5296 .token = try appendToken(c, .Keyword_anytype, "anytype"),
5208 .token = token_index,
5209 };5297 };
52105298
5211 (try fn_params.addOne()).* = .{5299 (try fn_params.addOne()).* = .{
...@@ -5213,7 +5301,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5213,7 +5301,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5213 .comptime_token = null,5301 .comptime_token = null,
5214 .noalias_token = null,5302 .noalias_token = null,
5215 .name_token = param_name_tok,5303 .name_token = param_name_tok,
5216 .param_type = .{ .type_expr = &identifier.base },5304 .param_type = .{ .any_type = &any_type.base },
5217 };5305 };
52185306
5219 if (it.peek().?.id != .Comma)5307 if (it.peek().?.id != .Comma)
...@@ -5236,24 +5324,6 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5236,24 +5324,6 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
52365324
5237 const type_of = try c.createBuiltinCall("@TypeOf", 1);5325 const type_of = try c.createBuiltinCall("@TypeOf", 1);
52385326
5239 const fn_proto = try ast.Node.FnProto.alloc(c.arena, fn_params.items.len);
5240 fn_proto.* = .{
5241 .visib_token = pub_tok,
5242 .extern_export_inline_token = inline_tok,
5243 .fn_token = fn_tok,
5244 .name_token = name_tok,
5245 .params_len = fn_params.items.len,
5246 .return_type = .{ .Explicit = &type_of.base },
5247 .doc_comments = null,
5248 .var_args_token = null,
5249 .body_node = null,
5250 .lib_name = null,
5251 .align_expr = null,
5252 .section_expr = null,
5253 .callconv_expr = null,
5254 };
5255 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
5256
5257 const return_expr = try transCreateNodeReturnExpr(c);5327 const return_expr = try transCreateNodeReturnExpr(c);
5258 const expr = try parseCExpr(c, it, source, source_loc, scope);5328 const expr = try parseCExpr(c, it, source, source_loc, scope);
5259 const last = it.next().?;5329 const last = it.next().?;
...@@ -5266,10 +5336,10 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5266,10 +5336,10 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5266 .{@tagName(last.id)},5336 .{@tagName(last.id)},
5267 );5337 );
5268 _ = try appendToken(c, .Semicolon, ";");5338 _ = try appendToken(c, .Semicolon, ";");
5269 const type_of_arg = if (expr.id != .Block) expr else blk: {5339 const type_of_arg = if (expr.tag != .Block) expr else blk: {
5270 const blk = @fieldParentPtr(ast.Node.Block, "base", expr);5340 const blk = @fieldParentPtr(ast.Node.Block, "base", expr);
5271 const blk_last = blk.statements()[blk.statements_len - 1];5341 const blk_last = blk.statements()[blk.statements_len - 1];
5272 std.debug.assert(blk_last.id == .ControlFlowExpression);5342 std.debug.assert(blk_last.tag == .ControlFlowExpression);
5273 const br = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", blk_last);5343 const br = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", blk_last);
5274 break :blk br.rhs.?;5344 break :blk br.rhs.?;
5275 };5345 };
...@@ -5279,7 +5349,18 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5279,7 +5349,18 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
52795349
5280 try block_scope.statements.append(&return_expr.base);5350 try block_scope.statements.append(&return_expr.base);
5281 const block_node = try block_scope.complete(c);5351 const block_node = try block_scope.complete(c);
5282 fn_proto.body_node = &block_node.base;5352 const fn_proto = try ast.Node.FnProto.create(c.arena, .{
5353 .fn_token = fn_tok,
5354 .params_len = fn_params.items.len,
5355 .return_type = .{ .Explicit = &type_of.base },
5356 }, .{
5357 .visib_token = pub_tok,
5358 .extern_export_inline_token = inline_tok,
5359 .name_token = name_tok,
5360 .body_node = &block_node.base,
5361 });
5362 mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
5363
5283 _ = try c.global_scope.macro_table.put(name, &fn_proto.base);5364 _ = try c.global_scope.macro_table.put(name, &fn_proto.base);
5284}5365}
52855366
...@@ -5320,11 +5401,11 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_...@@ -5320,11 +5401,11 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_
5320 // suppress result5401 // suppress result
5321 const lhs = try transCreateNodeIdentifier(c, "_");5402 const lhs = try transCreateNodeIdentifier(c, "_");
5322 const op_token = try appendToken(c, .Equal, "=");5403 const op_token = try appendToken(c, .Equal, "=");
5323 const op_node = try c.arena.create(ast.Node.InfixOp);5404 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
5324 op_node.* = .{5405 op_node.* = .{
5406 .base = .{ .tag = .Assign },
5325 .op_token = op_token,5407 .op_token = op_token,
5326 .lhs = lhs,5408 .lhs = lhs,
5327 .op = .Assign,
5328 .rhs = last,5409 .rhs = last,
5329 };5410 };
5330 try block_scope.statements.append(&op_node.base);5411 try block_scope.statements.append(&op_node.base);
...@@ -5668,161 +5749,23 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5668,161 +5749,23 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
56685749
5669 const lparen = try appendToken(c, .LParen, "(");5750 const lparen = try appendToken(c, .LParen, "(");
56705751
5671 if (saw_integer_literal) {5752 //(@import("std").meta.cast(dest, x))
5672 //( if (@typeInfo(dest) == .Pointer))5753 const import_fn_call = try c.createBuiltinCall("@import", 1);
5673 // @intToPtr(dest, x)5754 const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
5674 //else5755 import_fn_call.params()[0] = std_node;
5675 // @as(dest, x) )5756 import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
5676 const if_node = try transCreateNodeIf(c);5757 const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
5677 const type_info_node = try c.createBuiltinCall("@typeInfo", 1);5758 const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "cast");
5678 type_info_node.params()[0] = inner_node;
5679 type_info_node.rparen_token = try appendToken(c, .LParen, ")");
5680 const cmp_node = try c.arena.create(ast.Node.InfixOp);
5681 cmp_node.* = .{
5682 .op_token = try appendToken(c, .EqualEqual, "=="),
5683 .lhs = &type_info_node.base,
5684 .op = .EqualEqual,
5685 .rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
5686 };
5687 if_node.condition = &cmp_node.base;
5688 _ = try appendToken(c, .RParen, ")");
5689
5690 const int_to_ptr = try c.createBuiltinCall("@intToPtr", 2);
5691 int_to_ptr.params()[0] = inner_node;
5692 int_to_ptr.params()[1] = node_to_cast;
5693 int_to_ptr.rparen_token = try appendToken(c, .RParen, ")");
5694 if_node.body = &int_to_ptr.base;
5695
5696 const else_node = try transCreateNodeElse(c);
5697 if_node.@"else" = else_node;
5698
5699 const as_node = try c.createBuiltinCall("@as", 2);
5700 as_node.params()[0] = inner_node;
5701 as_node.params()[1] = node_to_cast;
5702 as_node.rparen_token = try appendToken(c, .RParen, ")");
5703 else_node.body = &as_node.base;
5704
5705 const group_node = try c.arena.create(ast.Node.GroupedExpression);
5706 group_node.* = .{
5707 .lparen = lparen,
5708 .expr = &if_node.base,
5709 .rparen = try appendToken(c, .RParen, ")"),
5710 };
5711 return &group_node.base;
5712 }
5713
5714 //( if (@typeInfo(@TypeOf(x)) == .Pointer)
5715 // @ptrCast(dest, @alignCast(@alignOf(dest.Child), x))
5716 //else if (@typeInfo(@TypeOf(x)) == .Int and @typeInfo(dest) == .Pointer))
5717 // @intToPtr(dest, x)
5718 //else
5719 // @as(dest, x) )
5720
5721 const if_1 = try transCreateNodeIf(c);
5722 const type_info_1 = try c.createBuiltinCall("@typeInfo", 1);
5723 const type_of_1 = try c.createBuiltinCall("@TypeOf", 1);
5724 type_info_1.params()[0] = &type_of_1.base;
5725 type_of_1.params()[0] = node_to_cast;
5726 type_of_1.rparen_token = try appendToken(c, .RParen, ")");
5727 type_info_1.rparen_token = try appendToken(c, .RParen, ")");
5728
5729 const cmp_1 = try c.arena.create(ast.Node.InfixOp);
5730 cmp_1.* = .{
5731 .op_token = try appendToken(c, .EqualEqual, "=="),
5732 .lhs = &type_info_1.base,
5733 .op = .EqualEqual,
5734 .rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
5735 };
5736 if_1.condition = &cmp_1.base;
5737 _ = try appendToken(c, .RParen, ")");
57385759
5739 const period_tok = try appendToken(c, .Period, ".");5760 const cast_fn_call = try c.createCall(outer_field_access, 2);
5740 const child_ident = try transCreateNodeIdentifier(c, "Child");5761 cast_fn_call.params()[0] = inner_node;
5741 const inner_node_child = try c.arena.create(ast.Node.InfixOp);5762 cast_fn_call.params()[1] = node_to_cast;
5742 inner_node_child.* = .{5763 cast_fn_call.rtoken = try appendToken(c, .RParen, ")");
5743 .op_token = period_tok,
5744 .lhs = inner_node,
5745 .op = .Period,
5746 .rhs = child_ident,
5747 };
5748
5749 const align_of = try c.createBuiltinCall("@alignOf", 1);
5750 align_of.params()[0] = &inner_node_child.base;
5751 align_of.rparen_token = try appendToken(c, .RParen, ")");
5752 // hack to get zig fmt to render a comma in builtin calls
5753 _ = try appendToken(c, .Comma, ",");
5754
5755 const align_cast = try c.createBuiltinCall("@alignCast", 2);
5756 align_cast.params()[0] = &align_of.base;
5757 align_cast.params()[1] = node_to_cast;
5758 align_cast.rparen_token = try appendToken(c, .RParen, ")");
5759
5760 const ptr_cast = try c.createBuiltinCall("@ptrCast", 2);
5761 ptr_cast.params()[0] = inner_node;
5762 ptr_cast.params()[1] = &align_cast.base;
5763 ptr_cast.rparen_token = try appendToken(c, .RParen, ")");
5764 if_1.body = &ptr_cast.base;
5765
5766 const else_1 = try transCreateNodeElse(c);
5767 if_1.@"else" = else_1;
5768
5769 const if_2 = try transCreateNodeIf(c);
5770 const type_info_2 = try c.createBuiltinCall("@typeInfo", 1);
5771 const type_of_2 = try c.createBuiltinCall("@TypeOf", 1);
5772 type_info_2.params()[0] = &type_of_2.base;
5773 type_of_2.params()[0] = node_to_cast;
5774 type_of_2.rparen_token = try appendToken(c, .RParen, ")");
5775 type_info_2.rparen_token = try appendToken(c, .RParen, ")");
5776
5777 const cmp_2 = try c.arena.create(ast.Node.InfixOp);
5778 cmp_2.* = .{
5779 .op_token = try appendToken(c, .EqualEqual, "=="),
5780 .lhs = &type_info_2.base,
5781 .op = .EqualEqual,
5782 .rhs = try transCreateNodeEnumLiteral(c, "Int"),
5783 };
5784 if_2.condition = &cmp_2.base;
5785 const cmp_4 = try c.arena.create(ast.Node.InfixOp);
5786 cmp_4.* = .{
5787 .op_token = try appendToken(c, .Keyword_and, "and"),
5788 .lhs = &cmp_2.base,
5789 .op = .BoolAnd,
5790 .rhs = undefined,
5791 };
5792 const type_info_3 = try c.createBuiltinCall("@typeInfo", 1);
5793 type_info_3.params()[0] = inner_node;
5794 type_info_3.rparen_token = try appendToken(c, .LParen, ")");
5795 const cmp_3 = try c.arena.create(ast.Node.InfixOp);
5796 cmp_3.* = .{
5797 .op_token = try appendToken(c, .EqualEqual, "=="),
5798 .lhs = &type_info_3.base,
5799 .op = .EqualEqual,
5800 .rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
5801 };
5802 cmp_4.rhs = &cmp_3.base;
5803 if_2.condition = &cmp_4.base;
5804 else_1.body = &if_2.base;
5805 _ = try appendToken(c, .RParen, ")");
5806
5807 const int_to_ptr = try c.createBuiltinCall("@intToPtr", 2);
5808 int_to_ptr.params()[0] = inner_node;
5809 int_to_ptr.params()[1] = node_to_cast;
5810 int_to_ptr.rparen_token = try appendToken(c, .RParen, ")");
5811 if_2.body = &int_to_ptr.base;
5812
5813 const else_2 = try transCreateNodeElse(c);
5814 if_2.@"else" = else_2;
5815
5816 const as = try c.createBuiltinCall("@as", 2);
5817 as.params()[0] = inner_node;
5818 as.params()[1] = node_to_cast;
5819 as.rparen_token = try appendToken(c, .RParen, ")");
5820 else_2.body = &as.base;
58215764
5822 const group_node = try c.arena.create(ast.Node.GroupedExpression);5765 const group_node = try c.arena.create(ast.Node.GroupedExpression);
5823 group_node.* = .{5766 group_node.* = .{
5824 .lparen = lparen,5767 .lparen = lparen,
5825 .expr = &if_1.base,5768 .expr = &cast_fn_call.base,
5826 .rparen = try appendToken(c, .RParen, ")"),5769 .rparen = try appendToken(c, .RParen, ")"),
5827 };5770 };
5828 return &group_node.base;5771 return &group_node.base;
...@@ -5841,9 +5784,60 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5841,9 +5784,60 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5841 }5784 }
5842}5785}
58435786
5787fn nodeIsInfixOp(tag: ast.Node.Tag) bool {
5788 return switch (tag) {
5789 .Add,
5790 .AddWrap,
5791 .ArrayCat,
5792 .ArrayMult,
5793 .Assign,
5794 .AssignBitAnd,
5795 .AssignBitOr,
5796 .AssignBitShiftLeft,
5797 .AssignBitShiftRight,
5798 .AssignBitXor,
5799 .AssignDiv,
5800 .AssignSub,
5801 .AssignSubWrap,
5802 .AssignMod,
5803 .AssignAdd,
5804 .AssignAddWrap,
5805 .AssignMul,
5806 .AssignMulWrap,
5807 .BangEqual,
5808 .BitAnd,
5809 .BitOr,
5810 .BitShiftLeft,
5811 .BitShiftRight,
5812 .BitXor,
5813 .BoolAnd,
5814 .BoolOr,
5815 .Div,
5816 .EqualEqual,
5817 .ErrorUnion,
5818 .GreaterOrEqual,
5819 .GreaterThan,
5820 .LessOrEqual,
5821 .LessThan,
5822 .MergeErrorSets,
5823 .Mod,
5824 .Mul,
5825 .MulWrap,
5826 .Period,
5827 .Range,
5828 .Sub,
5829 .SubWrap,
5830 .UnwrapOptional,
5831 .Catch,
5832 => true,
5833
5834 else => false,
5835 };
5836}
5837
5844fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {5838fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {
5845 if (!isBoolRes(node)) {5839 if (!isBoolRes(node)) {
5846 if (node.id != .InfixOp) return node;5840 if (!nodeIsInfixOp(node.tag)) return node;
58475841
5848 const group_node = try c.arena.create(ast.Node.GroupedExpression);5842 const group_node = try c.arena.create(ast.Node.GroupedExpression);
5849 group_node.* = .{5843 group_node.* = .{
...@@ -5862,7 +5856,7 @@ fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {...@@ -5862,7 +5856,7 @@ fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {
58625856
5863fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {5857fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
5864 if (isBoolRes(node)) {5858 if (isBoolRes(node)) {
5865 if (node.id != .InfixOp) return node;5859 if (!nodeIsInfixOp(node.tag)) return node;
58665860
5867 const group_node = try c.arena.create(ast.Node.GroupedExpression);5861 const group_node = try c.arena.create(ast.Node.GroupedExpression);
5868 group_node.* = .{5862 group_node.* = .{
...@@ -5875,11 +5869,11 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {...@@ -5875,11 +5869,11 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
58755869
5876 const op_token = try appendToken(c, .BangEqual, "!=");5870 const op_token = try appendToken(c, .BangEqual, "!=");
5877 const zero = try transCreateNodeInt(c, 0);5871 const zero = try transCreateNodeInt(c, 0);
5878 const res = try c.arena.create(ast.Node.InfixOp);5872 const res = try c.arena.create(ast.Node.SimpleInfixOp);
5879 res.* = .{5873 res.* = .{
5874 .base = .{ .tag = .BangEqual },
5880 .op_token = op_token,5875 .op_token = op_token,
5881 .lhs = node,5876 .lhs = node,
5882 .op = .BangEqual,
5883 .rhs = zero,5877 .rhs = zero,
5884 };5878 };
5885 const group_node = try c.arena.create(ast.Node.GroupedExpression);5879 const group_node = try c.arena.create(ast.Node.GroupedExpression);
...@@ -5896,7 +5890,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5896,7 +5890,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5896 while (true) {5890 while (true) {
5897 const tok = it.next().?;5891 const tok = it.next().?;
5898 var op_token: ast.TokenIndex = undefined;5892 var op_token: ast.TokenIndex = undefined;
5899 var op_id: ast.Node.InfixOp.Op = undefined;5893 var op_id: ast.Node.Tag = undefined;
5900 var bool_op = false;5894 var bool_op = false;
5901 switch (tok.id) {5895 switch (tok.id) {
5902 .Period => {5896 .Period => {
...@@ -5950,7 +5944,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -5950,7 +5944,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
5950 if (prev_id == .Keyword_void) {5944 if (prev_id == .Keyword_void) {
5951 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);5945 const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
5952 ptr.rhs = node;5946 ptr.rhs = node;
5953 const optional_node = try transCreateNodePrefixOp(c, .OptionalType, .QuestionMark, "?");5947 const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?");
5954 optional_node.rhs = &ptr.base;5948 optional_node.rhs = &ptr.base;
5955 return &optional_node.base;5949 return &optional_node.base;
5956 } else {5950 } else {
...@@ -6067,6 +6061,61 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -6067,6 +6061,61 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
6067 node = &call_node.base;6061 node = &call_node.base;
6068 continue;6062 continue;
6069 },6063 },
6064 .LBrace => {
6065 // must come immediately after `node`
6066 _ = try appendToken(c, .Comma, ",");
6067
6068 const dot = try appendToken(c, .Period, ".");
6069 _ = try appendToken(c, .LBrace, "{");
6070
6071 var init_vals = std.ArrayList(*ast.Node).init(c.gpa);
6072 defer init_vals.deinit();
6073
6074 while (true) {
6075 const val = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6076 try init_vals.append(val);
6077 const next = it.next().?;
6078 if (next.id == .Comma)
6079 _ = try appendToken(c, .Comma, ",")
6080 else if (next.id == .RBrace)
6081 break
6082 else {
6083 const first_tok = it.list.at(0);
6084 try failDecl(
6085 c,
6086 source_loc,
6087 source[first_tok.start..first_tok.end],
6088 "unable to translate C expr: expected ',' or '}}'",
6089 .{},
6090 );
6091 return error.ParseError;
6092 }
6093 }
6094 const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len);
6095 tuple_node.* = .{
6096 .dot = dot,
6097 .list_len = init_vals.items.len,
6098 .rtoken = try appendToken(c, .RBrace, "}"),
6099 };
6100 mem.copy(*ast.Node, tuple_node.list(), init_vals.items);
6101
6102
6103 //(@import("std").mem.zeroInit(T, .{x}))
6104 const import_fn_call = try c.createBuiltinCall("@import", 1);
6105 const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
6106 import_fn_call.params()[0] = std_node;
6107 import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
6108 const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem");
6109 const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroInit");
6110
6111 const zero_init_call = try c.createCall(outer_field_access, 2);
6112 zero_init_call.params()[0] = node;
6113 zero_init_call.params()[1] = &tuple_node.base;
6114 zero_init_call.rtoken = try appendToken(c, .RParen, ")");
6115
6116 node = &zero_init_call.base;
6117 continue;
6118 },
6070 .BangEqual => {6119 .BangEqual => {
6071 op_token = try appendToken(c, .BangEqual, "!=");6120 op_token = try appendToken(c, .BangEqual, "!=");
6072 op_id = .BangEqual;6121 op_id = .BangEqual;
...@@ -6103,11 +6152,11 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -6103,11 +6152,11 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
6103 const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;6152 const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;
6104 const lhs_node = try cast_fn(c, node);6153 const lhs_node = try cast_fn(c, node);
6105 const rhs_node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6154 const rhs_node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6106 const op_node = try c.arena.create(ast.Node.InfixOp);6155 const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
6107 op_node.* = .{6156 op_node.* = .{
6157 .base = .{ .tag = op_id },
6108 .op_token = op_token,6158 .op_token = op_token,
6109 .lhs = lhs_node,6159 .lhs = lhs_node,
6110 .op = op_id,
6111 .rhs = try cast_fn(c, rhs_node),6160 .rhs = try cast_fn(c, rhs_node),
6112 };6161 };
6113 node = &op_node.base;6162 node = &op_node.base;
...@@ -6119,18 +6168,18 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -6119,18 +6168,18 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
61196168
6120 switch (op_tok.id) {6169 switch (op_tok.id) {
6121 .Bang => {6170 .Bang => {
6122 const node = try transCreateNodePrefixOp(c, .BoolNot, .Bang, "!");6171 const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
6123 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6172 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6124 return &node.base;6173 return &node.base;
6125 },6174 },
6126 .Minus => {6175 .Minus => {
6127 const node = try transCreateNodePrefixOp(c, .Negation, .Minus, "-");6176 const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
6128 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6177 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6129 return &node.base;6178 return &node.base;
6130 },6179 },
6131 .Plus => return try parseCPrefixOpExpr(c, it, source, source_loc, scope),6180 .Plus => return try parseCPrefixOpExpr(c, it, source, source_loc, scope),
6132 .Tilde => {6181 .Tilde => {
6133 const node = try transCreateNodePrefixOp(c, .BitNot, .Tilde, "~");6182 const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
6134 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6183 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6135 return &node.base;6184 return &node.base;
6136 },6185 },
...@@ -6139,7 +6188,7 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,...@@ -6139,7 +6188,7 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
6139 return try transCreateNodePtrDeref(c, node);6188 return try transCreateNodePtrDeref(c, node);
6140 },6189 },
6141 .Ampersand => {6190 .Ampersand => {
6142 const node = try transCreateNodePrefixOp(c, .AddressOf, .Ampersand, "&");6191 const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
6143 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);6192 node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6144 return &node.base;6193 return &node.base;
6145 },6194 },
...@@ -6160,44 +6209,61 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {...@@ -6160,44 +6209,61 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {
6160}6209}
61616210
6162fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {6211fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
6163 if (node.id == .ContainerDecl) {6212 switch (node.tag) {
6164 return node;6213 .ContainerDecl,
6165 } else if (node.id == .PrefixOp) {6214 .AddressOf,
6166 return node;6215 .Await,
6167 } else if (node.cast(ast.Node.Identifier)) |ident| {6216 .BitNot,
6168 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| {6217 .BoolNot,
6169 if (kv.value.cast(ast.Node.VarDecl)) |var_decl|6218 .OptionalType,
6170 return getContainer(c, var_decl.init_node.?);6219 .Negation,
6171 }6220 .NegationWrap,
6172 } else if (node.cast(ast.Node.InfixOp)) |infix| {6221 .Resume,
6173 if (infix.op != .Period)6222 .Try,
6174 return null;6223 .ArrayType,
6175 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {6224 .ArrayTypeSentinel,
6176 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {6225 .PtrType,
6177 for (container.fieldsAndDecls()) |field_ref| {6226 .SliceType,
6178 const field = field_ref.cast(ast.Node.ContainerField).?;6227 => return node,
6179 const ident = infix.rhs.cast(ast.Node.Identifier).?;6228
6180 if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {6229 .Identifier => {
6181 return getContainer(c, field.type_expr.?);6230 const ident = node.cast(ast.Node.Identifier).?;
6231 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
6232 if (value.cast(ast.Node.VarDecl)) |var_decl|
6233 return getContainer(c, var_decl.getTrailer("init_node").?);
6234 }
6235 },
6236
6237 .Period => {
6238 const infix = node.castTag(.Period).?;
6239
6240 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
6241 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
6242 for (container.fieldsAndDecls()) |field_ref| {
6243 const field = field_ref.cast(ast.Node.ContainerField).?;
6244 const ident = infix.rhs.cast(ast.Node.Identifier).?;
6245 if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
6246 return getContainer(c, field.type_expr.?);
6247 }
6182 }6248 }
6183 }6249 }
6184 }6250 }
6185 }6251 },
6252
6253 else => {},
6186 }6254 }
6187 return null;6255 return null;
6188}6256}
61896257
6190fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {6258fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
6191 if (ref.cast(ast.Node.Identifier)) |ident| {6259 if (ref.cast(ast.Node.Identifier)) |ident| {
6192 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| {6260 if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
6193 if (kv.value.cast(ast.Node.VarDecl)) |var_decl| {6261 if (value.cast(ast.Node.VarDecl)) |var_decl| {
6194 if (var_decl.type_node) |ty|6262 if (var_decl.getTrailer("type_node")) |ty|
6195 return getContainer(c, ty);6263 return getContainer(c, ty);
6196 }6264 }
6197 }6265 }
6198 } else if (ref.cast(ast.Node.InfixOp)) |infix| {6266 } else if (ref.castTag(.Period)) |infix| {
6199 if (infix.op != .Period)
6200 return null;
6201 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {6267 if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
6202 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {6268 if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
6203 for (container.fieldsAndDecls()) |field_ref| {6269 for (container.fieldsAndDecls()) |field_ref| {
...@@ -6215,13 +6281,11 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {...@@ -6215,13 +6281,11 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
6215}6281}
62166282
6217fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {6283fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
6218 const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null;6284 const init = if (ref.cast(ast.Node.VarDecl)) |v| v.getTrailer("init_node").? else return null;
6219 if (getContainerTypeOf(c, init)) |ty_node| {6285 if (getContainerTypeOf(c, init)) |ty_node| {
6220 if (ty_node.cast(ast.Node.PrefixOp)) |prefix| {6286 if (ty_node.castTag(.OptionalType)) |prefix| {
6221 if (prefix.op == .OptionalType) {6287 if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
6222 if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {6288 return fn_proto;
6223 return fn_proto;
6224 }
6225 }6289 }
6226 }6290 }
6227 }6291 }
...@@ -6229,8 +6293,7 @@ fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {...@@ -6229,8 +6293,7 @@ fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
6229}6293}
62306294
6231fn addMacros(c: *Context) !void {6295fn addMacros(c: *Context) !void {
6232 var macro_it = c.global_scope.macro_table.iterator();6296 for (c.global_scope.macro_table.items()) |kv| {
6233 while (macro_it.next()) |kv| {
6234 if (getFnProto(c, kv.value)) |proto_node| {6297 if (getFnProto(c, kv.value)) |proto_node| {
6235 // If a macro aliases a global variable which is a function pointer, we conclude that6298 // If a macro aliases a global variable which is a function pointer, we conclude that
6236 // the macro is intended to represent a function that assumes the function pointer6299 // the macro is intended to represent a function that assumes the function pointer
src-self-hosted/type.zig+574-20
...@@ -21,8 +21,14 @@ pub const Type = extern union {...@@ -21,8 +21,14 @@ pub const Type = extern union {
21 switch (self.tag()) {21 switch (self.tag()) {
22 .u8,22 .u8,
23 .i8,23 .i8,
24 .isize,24 .u16,
25 .i16,
26 .u32,
27 .i32,
28 .u64,
29 .i64,
25 .usize,30 .usize,
31 .isize,
26 .c_short,32 .c_short,
27 .c_ushort,33 .c_ushort,
28 .c_int,34 .c_int,
...@@ -54,8 +60,10 @@ pub const Type = extern union {...@@ -54,8 +60,10 @@ pub const Type = extern union {
54 .@"undefined" => return .Undefined,60 .@"undefined" => return .Undefined,
5561
56 .fn_noreturn_no_args => return .Fn,62 .fn_noreturn_no_args => return .Fn,
63 .fn_void_no_args => return .Fn,
57 .fn_naked_noreturn_no_args => return .Fn,64 .fn_naked_noreturn_no_args => return .Fn,
58 .fn_ccc_void_no_args => return .Fn,65 .fn_ccc_void_no_args => return .Fn,
66 .function => return .Fn,
5967
60 .array, .array_u8_sentinel_0 => return .Array,68 .array, .array_u8_sentinel_0 => return .Array,
61 .single_const_pointer => return .Pointer,69 .single_const_pointer => return .Pointer,
...@@ -112,6 +120,12 @@ pub const Type = extern union {...@@ -112,6 +120,12 @@ pub const Type = extern union {
112 .Undefined => return true,120 .Undefined => return true,
113 .Null => return true,121 .Null => return true,
114 .Pointer => {122 .Pointer => {
123 // Hot path for common case:
124 if (a.cast(Payload.SingleConstPointer)) |a_payload| {
125 if (b.cast(Payload.SingleConstPointer)) |b_payload| {
126 return eql(a_payload.pointee_type, b_payload.pointee_type);
127 }
128 }
115 const is_slice_a = isSlice(a);129 const is_slice_a = isSlice(a);
116 const is_slice_b = isSlice(b);130 const is_slice_b = isSlice(b);
117 if (is_slice_a != is_slice_b)131 if (is_slice_a != is_slice_b)
...@@ -119,10 +133,14 @@ pub const Type = extern union {...@@ -119,10 +133,14 @@ pub const Type = extern union {
119 @panic("TODO implement more pointer Type equality comparison");133 @panic("TODO implement more pointer Type equality comparison");
120 },134 },
121 .Int => {135 .Int => {
122 if (a.tag() != b.tag()) {136 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
123 // Detect that e.g. u64 != usize, even if the bits match on a particular target.137 const a_is_named_int = a.isNamedInt();
138 const b_is_named_int = b.isNamedInt();
139 if (a_is_named_int != b_is_named_int)
124 return false;140 return false;
125 }141 if (a_is_named_int)
142 return a.tag() == b.tag();
143 // Remaining cases are arbitrary sized integers.
126 // The target will not be branched upon, because we handled target-dependent cases above.144 // The target will not be branched upon, because we handled target-dependent cases above.
127 const info_a = a.intInfo(@as(Target, undefined));145 const info_a = a.intInfo(@as(Target, undefined));
128 const info_b = b.intInfo(@as(Target, undefined));146 const info_b = b.intInfo(@as(Target, undefined));
...@@ -145,6 +163,22 @@ pub const Type = extern union {...@@ -145,6 +163,22 @@ pub const Type = extern union {
145 return sentinel_b == null;163 return sentinel_b == null;
146 }164 }
147 },165 },
166 .Fn => {
167 if (!a.fnReturnType().eql(b.fnReturnType()))
168 return false;
169 if (a.fnCallingConvention() != b.fnCallingConvention())
170 return false;
171 const a_param_len = a.fnParamLen();
172 const b_param_len = b.fnParamLen();
173 if (a_param_len != b_param_len)
174 return false;
175 var i: usize = 0;
176 while (i < a_param_len) : (i += 1) {
177 if (!a.fnParamType(i).eql(b.fnParamType(i)))
178 return false;
179 }
180 return true;
181 },
148 .Float,182 .Float,
149 .Struct,183 .Struct,
150 .Optional,184 .Optional,
...@@ -152,23 +186,114 @@ pub const Type = extern union {...@@ -152,23 +186,114 @@ pub const Type = extern union {
152 .ErrorSet,186 .ErrorSet,
153 .Enum,187 .Enum,
154 .Union,188 .Union,
155 .Fn,
156 .BoundFn,189 .BoundFn,
157 .Opaque,190 .Opaque,
158 .Frame,191 .Frame,
159 .AnyFrame,192 .AnyFrame,
160 .Vector,193 .Vector,
161 .EnumLiteral,194 .EnumLiteral,
162 => @panic("TODO implement more Type equality comparison"),195 => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
196 }
197 }
198
199 pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type {
200 if (self.tag_if_small_enough < Tag.no_payload_count) {
201 return Type{ .tag_if_small_enough = self.tag_if_small_enough };
202 } else switch (self.ptr_otherwise.tag) {
203 .u8,
204 .i8,
205 .u16,
206 .i16,
207 .u32,
208 .i32,
209 .u64,
210 .i64,
211 .usize,
212 .isize,
213 .c_short,
214 .c_ushort,
215 .c_int,
216 .c_uint,
217 .c_long,
218 .c_ulong,
219 .c_longlong,
220 .c_ulonglong,
221 .c_longdouble,
222 .c_void,
223 .f16,
224 .f32,
225 .f64,
226 .f128,
227 .bool,
228 .void,
229 .type,
230 .anyerror,
231 .comptime_int,
232 .comptime_float,
233 .noreturn,
234 .@"null",
235 .@"undefined",
236 .fn_noreturn_no_args,
237 .fn_void_no_args,
238 .fn_naked_noreturn_no_args,
239 .fn_ccc_void_no_args,
240 .single_const_pointer_to_comptime_int,
241 .const_slice_u8,
242 => unreachable,
243
244 .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
245 .array => {
246 const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
247 const new_payload = try allocator.create(Payload.Array);
248 new_payload.* = .{
249 .base = payload.base,
250 .len = payload.len,
251 .elem_type = try payload.elem_type.copy(allocator),
252 };
253 return Type{ .ptr_otherwise = &new_payload.base };
254 },
255 .single_const_pointer => {
256 const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise);
257 const new_payload = try allocator.create(Payload.SingleConstPointer);
258 new_payload.* = .{
259 .base = payload.base,
260 .pointee_type = try payload.pointee_type.copy(allocator),
261 };
262 return Type{ .ptr_otherwise = &new_payload.base };
263 },
264 .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
265 .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
266 .function => {
267 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
268 const new_payload = try allocator.create(Payload.Function);
269 const param_types = try allocator.alloc(Type, payload.param_types.len);
270 for (payload.param_types) |param_type, i| {
271 param_types[i] = try param_type.copy(allocator);
272 }
273 new_payload.* = .{
274 .base = payload.base,
275 .return_type = try payload.return_type.copy(allocator),
276 .param_types = param_types,
277 .cc = payload.cc,
278 };
279 return Type{ .ptr_otherwise = &new_payload.base };
280 },
163 }281 }
164 }282 }
165283
284 fn copyPayloadShallow(self: Type, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Type {
285 const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
286 const new_payload = try allocator.create(T);
287 new_payload.* = payload.*;
288 return Type{ .ptr_otherwise = &new_payload.base };
289 }
290
166 pub fn format(291 pub fn format(
167 self: Type,292 self: Type,
168 comptime fmt: []const u8,293 comptime fmt: []const u8,
169 options: std.fmt.FormatOptions,294 options: std.fmt.FormatOptions,
170 out_stream: var,295 out_stream: anytype,
171 ) !void {296 ) @TypeOf(out_stream).Error!void {
172 comptime assert(fmt.len == 0);297 comptime assert(fmt.len == 0);
173 var ty = self;298 var ty = self;
174 while (true) {299 while (true) {
...@@ -176,8 +301,14 @@ pub const Type = extern union {...@@ -176,8 +301,14 @@ pub const Type = extern union {
176 switch (t) {301 switch (t) {
177 .u8,302 .u8,
178 .i8,303 .i8,
179 .isize,304 .u16,
305 .i16,
306 .u32,
307 .i32,
308 .u64,
309 .i64,
180 .usize,310 .usize,
311 .isize,
181 .c_short,312 .c_short,
182 .c_ushort,313 .c_ushort,
183 .c_int,314 .c_int,
...@@ -206,9 +337,20 @@ pub const Type = extern union {...@@ -206,9 +337,20 @@ pub const Type = extern union {
206337
207 .const_slice_u8 => return out_stream.writeAll("[]const u8"),338 .const_slice_u8 => return out_stream.writeAll("[]const u8"),
208 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),339 .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
340 .fn_void_no_args => return out_stream.writeAll("fn() void"),
209 .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),341 .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
210 .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),342 .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
211 .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),343 .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
344 .function => {
345 const payload = @fieldParentPtr(Payload.Function, "base", ty.ptr_otherwise);
346 try out_stream.writeAll("fn(");
347 for (payload.param_types) |param_type, i| {
348 if (i != 0) try out_stream.writeAll(", ");
349 try param_type.format("", .{}, out_stream);
350 }
351 try out_stream.writeAll(") ");
352 try payload.return_type.format("", .{}, out_stream);
353 },
212354
213 .array_u8_sentinel_0 => {355 .array_u8_sentinel_0 => {
214 const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);356 const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
...@@ -243,8 +385,14 @@ pub const Type = extern union {...@@ -243,8 +385,14 @@ pub const Type = extern union {
243 switch (self.tag()) {385 switch (self.tag()) {
244 .u8 => return Value.initTag(.u8_type),386 .u8 => return Value.initTag(.u8_type),
245 .i8 => return Value.initTag(.i8_type),387 .i8 => return Value.initTag(.i8_type),
246 .isize => return Value.initTag(.isize_type),388 .u16 => return Value.initTag(.u16_type),
389 .i16 => return Value.initTag(.i16_type),
390 .u32 => return Value.initTag(.u32_type),
391 .i32 => return Value.initTag(.i32_type),
392 .u64 => return Value.initTag(.u64_type),
393 .i64 => return Value.initTag(.i64_type),
247 .usize => return Value.initTag(.usize_type),394 .usize => return Value.initTag(.usize_type),
395 .isize => return Value.initTag(.isize_type),
248 .c_short => return Value.initTag(.c_short_type),396 .c_short => return Value.initTag(.c_short_type),
249 .c_ushort => return Value.initTag(.c_ushort_type),397 .c_ushort => return Value.initTag(.c_ushort_type),
250 .c_int => return Value.initTag(.c_int_type),398 .c_int => return Value.initTag(.c_int_type),
...@@ -269,6 +417,7 @@ pub const Type = extern union {...@@ -269,6 +417,7 @@ pub const Type = extern union {
269 .@"null" => return Value.initTag(.null_type),417 .@"null" => return Value.initTag(.null_type),
270 .@"undefined" => return Value.initTag(.undefined_type),418 .@"undefined" => return Value.initTag(.undefined_type),
271 .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),419 .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
420 .fn_void_no_args => return Value.initTag(.fn_void_no_args_type),
272 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),421 .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
273 .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),422 .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
274 .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),423 .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
...@@ -285,8 +434,14 @@ pub const Type = extern union {...@@ -285,8 +434,14 @@ pub const Type = extern union {
285 return switch (self.tag()) {434 return switch (self.tag()) {
286 .u8,435 .u8,
287 .i8,436 .i8,
288 .isize,437 .u16,
438 .i16,
439 .u32,
440 .i32,
441 .u64,
442 .i64,
289 .usize,443 .usize,
444 .isize,
290 .c_short,445 .c_short,
291 .c_ushort,446 .c_ushort,
292 .c_int,447 .c_int,
...@@ -303,8 +458,10 @@ pub const Type = extern union {...@@ -303,8 +458,10 @@ pub const Type = extern union {
303 .bool,458 .bool,
304 .anyerror,459 .anyerror,
305 .fn_noreturn_no_args,460 .fn_noreturn_no_args,
461 .fn_void_no_args,
306 .fn_naked_noreturn_no_args,462 .fn_naked_noreturn_no_args,
307 .fn_ccc_void_no_args,463 .fn_ccc_void_no_args,
464 .function,
308 .single_const_pointer_to_comptime_int,465 .single_const_pointer_to_comptime_int,
309 .const_slice_u8,466 .const_slice_u8,
310 .array_u8_sentinel_0,467 .array_u8_sentinel_0,
...@@ -326,6 +483,10 @@ pub const Type = extern union {...@@ -326,6 +483,10 @@ pub const Type = extern union {
326 };483 };
327 }484 }
328485
486 pub fn isNoReturn(self: Type) bool {
487 return self.zigTypeTag() == .NoReturn;
488 }
489
329 /// Asserts that hasCodeGenBits() is true.490 /// Asserts that hasCodeGenBits() is true.
330 pub fn abiAlignment(self: Type, target: Target) u32 {491 pub fn abiAlignment(self: Type, target: Target) u32 {
331 return switch (self.tag()) {492 return switch (self.tag()) {
...@@ -333,11 +494,17 @@ pub const Type = extern union {...@@ -333,11 +494,17 @@ pub const Type = extern union {
333 .i8,494 .i8,
334 .bool,495 .bool,
335 .fn_noreturn_no_args, // represents machine code; not a pointer496 .fn_noreturn_no_args, // represents machine code; not a pointer
497 .fn_void_no_args, // represents machine code; not a pointer
336 .fn_naked_noreturn_no_args, // represents machine code; not a pointer498 .fn_naked_noreturn_no_args, // represents machine code; not a pointer
337 .fn_ccc_void_no_args, // represents machine code; not a pointer499 .fn_ccc_void_no_args, // represents machine code; not a pointer
500 .function, // represents machine code; not a pointer
338 .array_u8_sentinel_0,501 .array_u8_sentinel_0,
339 => return 1,502 => return 1,
340503
504 .i16, .u16 => return 2,
505 .i32, .u32 => return 4,
506 .i64, .u64 => return 8,
507
341 .isize,508 .isize,
342 .usize,509 .usize,
343 .single_const_pointer_to_comptime_int,510 .single_const_pointer_to_comptime_int,
...@@ -387,12 +554,87 @@ pub const Type = extern union {...@@ -387,12 +554,87 @@ pub const Type = extern union {
387 };554 };
388 }555 }
389556
390 pub fn isSinglePointer(self: Type) bool {557 /// Asserts the type has the ABI size already resolved.
558 pub fn abiSize(self: Type, target: Target) u64 {
391 return switch (self.tag()) {559 return switch (self.tag()) {
560 .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer
561 .fn_void_no_args => unreachable, // represents machine code; not a pointer
562 .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer
563 .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
564 .function => unreachable, // represents machine code; not a pointer
565 .c_void => unreachable,
566 .void => unreachable,
567 .type => unreachable,
568 .comptime_int => unreachable,
569 .comptime_float => unreachable,
570 .noreturn => unreachable,
571 .@"null" => unreachable,
572 .@"undefined" => unreachable,
573
392 .u8,574 .u8,
393 .i8,575 .i8,
576 .bool,
577 => return 1,
578
579 .array_u8_sentinel_0 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len,
580 .array => {
581 const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
582 const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
583 return payload.len * elem_size;
584 },
585 .i16, .u16 => return 2,
586 .i32, .u32 => return 4,
587 .i64, .u64 => return 8,
588
394 .isize,589 .isize,
395 .usize,590 .usize,
591 .single_const_pointer_to_comptime_int,
592 .const_slice_u8,
593 .single_const_pointer,
594 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
595
596 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
597 .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
598 .c_int => return @divExact(CType.int.sizeInBits(target), 8),
599 .c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
600 .c_long => return @divExact(CType.long.sizeInBits(target), 8),
601 .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
602 .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
603 .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
604
605 .f16 => return 2,
606 .f32 => return 4,
607 .f64 => return 8,
608 .f128 => return 16,
609 .c_longdouble => return 16,
610
611 .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type
612
613 .int_signed, .int_unsigned => {
614 const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
615 pl.bits
616 else if (self.cast(Payload.IntUnsigned)) |pl|
617 pl.bits
618 else
619 unreachable;
620
621 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
622 },
623 };
624 }
625
626 pub fn isSinglePointer(self: Type) bool {
627 return switch (self.tag()) {
628 .u8,
629 .i8,
630 .u16,
631 .i16,
632 .u32,
633 .i32,
634 .u64,
635 .i64,
636 .usize,
637 .isize,
396 .c_short,638 .c_short,
397 .c_ushort,639 .c_ushort,
398 .c_int,640 .c_int,
...@@ -420,8 +662,10 @@ pub const Type = extern union {...@@ -420,8 +662,10 @@ pub const Type = extern union {
420 .array_u8_sentinel_0,662 .array_u8_sentinel_0,
421 .const_slice_u8,663 .const_slice_u8,
422 .fn_noreturn_no_args,664 .fn_noreturn_no_args,
665 .fn_void_no_args,
423 .fn_naked_noreturn_no_args,666 .fn_naked_noreturn_no_args,
424 .fn_ccc_void_no_args,667 .fn_ccc_void_no_args,
668 .function,
425 .int_unsigned,669 .int_unsigned,
426 .int_signed,670 .int_signed,
427 => false,671 => false,
...@@ -436,8 +680,14 @@ pub const Type = extern union {...@@ -436,8 +680,14 @@ pub const Type = extern union {
436 return switch (self.tag()) {680 return switch (self.tag()) {
437 .u8,681 .u8,
438 .i8,682 .i8,
439 .isize,683 .u16,
684 .i16,
685 .u32,
686 .i32,
687 .u64,
688 .i64,
440 .usize,689 .usize,
690 .isize,
441 .c_short,691 .c_short,
442 .c_ushort,692 .c_ushort,
443 .c_int,693 .c_int,
...@@ -466,8 +716,10 @@ pub const Type = extern union {...@@ -466,8 +716,10 @@ pub const Type = extern union {
466 .single_const_pointer,716 .single_const_pointer,
467 .single_const_pointer_to_comptime_int,717 .single_const_pointer_to_comptime_int,
468 .fn_noreturn_no_args,718 .fn_noreturn_no_args,
719 .fn_void_no_args,
469 .fn_naked_noreturn_no_args,720 .fn_naked_noreturn_no_args,
470 .fn_ccc_void_no_args,721 .fn_ccc_void_no_args,
722 .function,
471 .int_unsigned,723 .int_unsigned,
472 .int_signed,724 .int_signed,
473 => false,725 => false,
...@@ -481,8 +733,14 @@ pub const Type = extern union {...@@ -481,8 +733,14 @@ pub const Type = extern union {
481 return switch (self.tag()) {733 return switch (self.tag()) {
482 .u8,734 .u8,
483 .i8,735 .i8,
484 .isize,736 .u16,
737 .i16,
738 .u32,
739 .i32,
740 .u64,
741 .i64,
485 .usize,742 .usize,
743 .isize,
486 .c_short,744 .c_short,
487 .c_ushort,745 .c_ushort,
488 .c_int,746 .c_int,
...@@ -509,8 +767,10 @@ pub const Type = extern union {...@@ -509,8 +767,10 @@ pub const Type = extern union {
509 .array,767 .array,
510 .array_u8_sentinel_0,768 .array_u8_sentinel_0,
511 .fn_noreturn_no_args,769 .fn_noreturn_no_args,
770 .fn_void_no_args,
512 .fn_naked_noreturn_no_args,771 .fn_naked_noreturn_no_args,
513 .fn_ccc_void_no_args,772 .fn_ccc_void_no_args,
773 .function,
514 .int_unsigned,774 .int_unsigned,
515 .int_signed,775 .int_signed,
516 => unreachable,776 => unreachable,
...@@ -527,8 +787,14 @@ pub const Type = extern union {...@@ -527,8 +787,14 @@ pub const Type = extern union {
527 return switch (self.tag()) {787 return switch (self.tag()) {
528 .u8,788 .u8,
529 .i8,789 .i8,
530 .isize,790 .u16,
791 .i16,
792 .u32,
793 .i32,
794 .u64,
795 .i64,
531 .usize,796 .usize,
797 .isize,
532 .c_short,798 .c_short,
533 .c_ushort,799 .c_ushort,
534 .c_int,800 .c_int,
...@@ -553,8 +819,10 @@ pub const Type = extern union {...@@ -553,8 +819,10 @@ pub const Type = extern union {
553 .@"null",819 .@"null",
554 .@"undefined",820 .@"undefined",
555 .fn_noreturn_no_args,821 .fn_noreturn_no_args,
822 .fn_void_no_args,
556 .fn_naked_noreturn_no_args,823 .fn_naked_noreturn_no_args,
557 .fn_ccc_void_no_args,824 .fn_ccc_void_no_args,
825 .function,
558 .int_unsigned,826 .int_unsigned,
559 .int_signed,827 .int_signed,
560 => unreachable,828 => unreachable,
...@@ -571,8 +839,14 @@ pub const Type = extern union {...@@ -571,8 +839,14 @@ pub const Type = extern union {
571 return switch (self.tag()) {839 return switch (self.tag()) {
572 .u8,840 .u8,
573 .i8,841 .i8,
574 .isize,842 .u16,
843 .i16,
844 .u32,
845 .i32,
846 .u64,
847 .i64,
575 .usize,848 .usize,
849 .isize,
576 .c_short,850 .c_short,
577 .c_ushort,851 .c_ushort,
578 .c_int,852 .c_int,
...@@ -597,8 +871,10 @@ pub const Type = extern union {...@@ -597,8 +871,10 @@ pub const Type = extern union {
597 .@"null",871 .@"null",
598 .@"undefined",872 .@"undefined",
599 .fn_noreturn_no_args,873 .fn_noreturn_no_args,
874 .fn_void_no_args,
600 .fn_naked_noreturn_no_args,875 .fn_naked_noreturn_no_args,
601 .fn_ccc_void_no_args,876 .fn_ccc_void_no_args,
877 .function,
602 .single_const_pointer,878 .single_const_pointer,
603 .single_const_pointer_to_comptime_int,879 .single_const_pointer_to_comptime_int,
604 .const_slice_u8,880 .const_slice_u8,
...@@ -616,8 +892,14 @@ pub const Type = extern union {...@@ -616,8 +892,14 @@ pub const Type = extern union {
616 return switch (self.tag()) {892 return switch (self.tag()) {
617 .u8,893 .u8,
618 .i8,894 .i8,
619 .isize,895 .u16,
896 .i16,
897 .u32,
898 .i32,
899 .u64,
900 .i64,
620 .usize,901 .usize,
902 .isize,
621 .c_short,903 .c_short,
622 .c_ushort,904 .c_ushort,
623 .c_int,905 .c_int,
...@@ -642,8 +924,10 @@ pub const Type = extern union {...@@ -642,8 +924,10 @@ pub const Type = extern union {
642 .@"null",924 .@"null",
643 .@"undefined",925 .@"undefined",
644 .fn_noreturn_no_args,926 .fn_noreturn_no_args,
927 .fn_void_no_args,
645 .fn_naked_noreturn_no_args,928 .fn_naked_noreturn_no_args,
646 .fn_ccc_void_no_args,929 .fn_ccc_void_no_args,
930 .function,
647 .single_const_pointer,931 .single_const_pointer,
648 .single_const_pointer_to_comptime_int,932 .single_const_pointer_to_comptime_int,
649 .const_slice_u8,933 .const_slice_u8,
...@@ -656,6 +940,11 @@ pub const Type = extern union {...@@ -656,6 +940,11 @@ pub const Type = extern union {
656 };940 };
657 }941 }
658942
943 /// Returns true if and only if the type is a fixed-width integer.
944 pub fn isInt(self: Type) bool {
945 return self.isSignedInt() or self.isUnsignedInt();
946 }
947
659 /// Returns true if and only if the type is a fixed-width, signed integer.948 /// Returns true if and only if the type is a fixed-width, signed integer.
660 pub fn isSignedInt(self: Type) bool {949 pub fn isSignedInt(self: Type) bool {
661 return switch (self.tag()) {950 return switch (self.tag()) {
...@@ -675,8 +964,10 @@ pub const Type = extern union {...@@ -675,8 +964,10 @@ pub const Type = extern union {
675 .@"null",964 .@"null",
676 .@"undefined",965 .@"undefined",
677 .fn_noreturn_no_args,966 .fn_noreturn_no_args,
967 .fn_void_no_args,
678 .fn_naked_noreturn_no_args,968 .fn_naked_noreturn_no_args,
679 .fn_ccc_void_no_args,969 .fn_ccc_void_no_args,
970 .function,
680 .array,971 .array,
681 .single_const_pointer,972 .single_const_pointer,
682 .single_const_pointer_to_comptime_int,973 .single_const_pointer_to_comptime_int,
...@@ -689,6 +980,9 @@ pub const Type = extern union {...@@ -689,6 +980,9 @@ pub const Type = extern union {
689 .c_uint,980 .c_uint,
690 .c_ulong,981 .c_ulong,
691 .c_ulonglong,982 .c_ulonglong,
983 .u16,
984 .u32,
985 .u64,
692 => false,986 => false,
693987
694 .int_signed,988 .int_signed,
...@@ -698,11 +992,68 @@ pub const Type = extern union {...@@ -698,11 +992,68 @@ pub const Type = extern union {
698 .c_int,992 .c_int,
699 .c_long,993 .c_long,
700 .c_longlong,994 .c_longlong,
995 .i16,
996 .i32,
997 .i64,
701 => true,998 => true,
702 };999 };
703 }1000 }
7041001
705 /// Asserts the type is a fixed-width integer.1002 /// Returns true if and only if the type is a fixed-width, unsigned integer.
1003 pub fn isUnsignedInt(self: Type) bool {
1004 return switch (self.tag()) {
1005 .f16,
1006 .f32,
1007 .f64,
1008 .f128,
1009 .c_longdouble,
1010 .c_void,
1011 .bool,
1012 .void,
1013 .type,
1014 .anyerror,
1015 .comptime_int,
1016 .comptime_float,
1017 .noreturn,
1018 .@"null",
1019 .@"undefined",
1020 .fn_noreturn_no_args,
1021 .fn_void_no_args,
1022 .fn_naked_noreturn_no_args,
1023 .fn_ccc_void_no_args,
1024 .function,
1025 .array,
1026 .single_const_pointer,
1027 .single_const_pointer_to_comptime_int,
1028 .array_u8_sentinel_0,
1029 .const_slice_u8,
1030 .int_signed,
1031 .i8,
1032 .isize,
1033 .c_short,
1034 .c_int,
1035 .c_long,
1036 .c_longlong,
1037 .i16,
1038 .i32,
1039 .i64,
1040 => false,
1041
1042 .int_unsigned,
1043 .u8,
1044 .usize,
1045 .c_ushort,
1046 .c_uint,
1047 .c_ulong,
1048 .c_ulonglong,
1049 .u16,
1050 .u32,
1051 .u64,
1052 => true,
1053 };
1054 }
1055
1056 /// Asserts the type is an integer.
706 pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {1057 pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } {
707 return switch (self.tag()) {1058 return switch (self.tag()) {
708 .f16,1059 .f16,
...@@ -721,8 +1072,10 @@ pub const Type = extern union {...@@ -721,8 +1072,10 @@ pub const Type = extern union {
721 .@"null",1072 .@"null",
722 .@"undefined",1073 .@"undefined",
723 .fn_noreturn_no_args,1074 .fn_noreturn_no_args,
1075 .fn_void_no_args,
724 .fn_naked_noreturn_no_args,1076 .fn_naked_noreturn_no_args,
725 .fn_ccc_void_no_args,1077 .fn_ccc_void_no_args,
1078 .function,
726 .array,1079 .array,
727 .single_const_pointer,1080 .single_const_pointer,
728 .single_const_pointer_to_comptime_int,1081 .single_const_pointer_to_comptime_int,
...@@ -734,6 +1087,12 @@ pub const Type = extern union {...@@ -734,6 +1087,12 @@ pub const Type = extern union {
734 .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits },1087 .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits },
735 .u8 => .{ .signed = false, .bits = 8 },1088 .u8 => .{ .signed = false, .bits = 8 },
736 .i8 => .{ .signed = true, .bits = 8 },1089 .i8 => .{ .signed = true, .bits = 8 },
1090 .u16 => .{ .signed = false, .bits = 16 },
1091 .i16 => .{ .signed = true, .bits = 16 },
1092 .u32 => .{ .signed = false, .bits = 32 },
1093 .i32 => .{ .signed = true, .bits = 32 },
1094 .u64 => .{ .signed = false, .bits = 64 },
1095 .i64 => .{ .signed = true, .bits = 64 },
737 .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },1096 .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() },
738 .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },1097 .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() },
739 .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) },1098 .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) },
...@@ -747,6 +1106,59 @@ pub const Type = extern union {...@@ -747,6 +1106,59 @@ pub const Type = extern union {
747 };1106 };
748 }1107 }
7491108
1109 pub fn isNamedInt(self: Type) bool {
1110 return switch (self.tag()) {
1111 .f16,
1112 .f32,
1113 .f64,
1114 .f128,
1115 .c_longdouble,
1116 .c_void,
1117 .bool,
1118 .void,
1119 .type,
1120 .anyerror,
1121 .comptime_int,
1122 .comptime_float,
1123 .noreturn,
1124 .@"null",
1125 .@"undefined",
1126 .fn_noreturn_no_args,
1127 .fn_void_no_args,
1128 .fn_naked_noreturn_no_args,
1129 .fn_ccc_void_no_args,
1130 .function,
1131 .array,
1132 .single_const_pointer,
1133 .single_const_pointer_to_comptime_int,
1134 .array_u8_sentinel_0,
1135 .const_slice_u8,
1136 .int_unsigned,
1137 .int_signed,
1138 .u8,
1139 .i8,
1140 .u16,
1141 .i16,
1142 .u32,
1143 .i32,
1144 .u64,
1145 .i64,
1146 => false,
1147
1148 .usize,
1149 .isize,
1150 .c_short,
1151 .c_ushort,
1152 .c_int,
1153 .c_uint,
1154 .c_long,
1155 .c_ulong,
1156 .c_longlong,
1157 .c_ulonglong,
1158 => true,
1159 };
1160 }
1161
750 pub fn isFloat(self: Type) bool {1162 pub fn isFloat(self: Type) bool {
751 return switch (self.tag()) {1163 return switch (self.tag()) {
752 .f16,1164 .f16,
...@@ -777,8 +1189,10 @@ pub const Type = extern union {...@@ -777,8 +1189,10 @@ pub const Type = extern union {
777 pub fn fnParamLen(self: Type) usize {1189 pub fn fnParamLen(self: Type) usize {
778 return switch (self.tag()) {1190 return switch (self.tag()) {
779 .fn_noreturn_no_args => 0,1191 .fn_noreturn_no_args => 0,
1192 .fn_void_no_args => 0,
780 .fn_naked_noreturn_no_args => 0,1193 .fn_naked_noreturn_no_args => 0,
781 .fn_ccc_void_no_args => 0,1194 .fn_ccc_void_no_args => 0,
1195 .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).param_types.len,
7821196
783 .f16,1197 .f16,
784 .f32,1198 .f32,
...@@ -802,6 +1216,12 @@ pub const Type = extern union {...@@ -802,6 +1216,12 @@ pub const Type = extern union {
802 .const_slice_u8,1216 .const_slice_u8,
803 .u8,1217 .u8,
804 .i8,1218 .i8,
1219 .u16,
1220 .i16,
1221 .u32,
1222 .i32,
1223 .u64,
1224 .i64,
805 .usize,1225 .usize,
806 .isize,1226 .isize,
807 .c_short,1227 .c_short,
...@@ -823,8 +1243,13 @@ pub const Type = extern union {...@@ -823,8 +1243,13 @@ pub const Type = extern union {
823 pub fn fnParamTypes(self: Type, types: []Type) void {1243 pub fn fnParamTypes(self: Type, types: []Type) void {
824 switch (self.tag()) {1244 switch (self.tag()) {
825 .fn_noreturn_no_args => return,1245 .fn_noreturn_no_args => return,
1246 .fn_void_no_args => return,
826 .fn_naked_noreturn_no_args => return,1247 .fn_naked_noreturn_no_args => return,
827 .fn_ccc_void_no_args => return,1248 .fn_ccc_void_no_args => return,
1249 .function => {
1250 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
1251 std.mem.copy(Type, types, payload.param_types);
1252 },
8281253
829 .f16,1254 .f16,
830 .f32,1255 .f32,
...@@ -848,6 +1273,68 @@ pub const Type = extern union {...@@ -848,6 +1273,68 @@ pub const Type = extern union {
848 .const_slice_u8,1273 .const_slice_u8,
849 .u8,1274 .u8,
850 .i8,1275 .i8,
1276 .u16,
1277 .i16,
1278 .u32,
1279 .i32,
1280 .u64,
1281 .i64,
1282 .usize,
1283 .isize,
1284 .c_short,
1285 .c_ushort,
1286 .c_int,
1287 .c_uint,
1288 .c_long,
1289 .c_ulong,
1290 .c_longlong,
1291 .c_ulonglong,
1292 .int_unsigned,
1293 .int_signed,
1294 => unreachable,
1295 }
1296 }
1297
1298 /// Asserts the type is a function.
1299 pub fn fnParamType(self: Type, index: usize) Type {
1300 switch (self.tag()) {
1301 .function => {
1302 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
1303 return payload.param_types[index];
1304 },
1305
1306 .fn_noreturn_no_args,
1307 .fn_void_no_args,
1308 .fn_naked_noreturn_no_args,
1309 .fn_ccc_void_no_args,
1310 .f16,
1311 .f32,
1312 .f64,
1313 .f128,
1314 .c_longdouble,
1315 .c_void,
1316 .bool,
1317 .void,
1318 .type,
1319 .anyerror,
1320 .comptime_int,
1321 .comptime_float,
1322 .noreturn,
1323 .@"null",
1324 .@"undefined",
1325 .array,
1326 .single_const_pointer,
1327 .single_const_pointer_to_comptime_int,
1328 .array_u8_sentinel_0,
1329 .const_slice_u8,
1330 .u8,
1331 .i8,
1332 .u16,
1333 .i16,
1334 .u32,
1335 .i32,
1336 .u64,
1337 .i64,
851 .usize,1338 .usize,
852 .isize,1339 .isize,
853 .c_short,1340 .c_short,
...@@ -869,7 +1356,12 @@ pub const Type = extern union {...@@ -869,7 +1356,12 @@ pub const Type = extern union {
869 return switch (self.tag()) {1356 return switch (self.tag()) {
870 .fn_noreturn_no_args => Type.initTag(.noreturn),1357 .fn_noreturn_no_args => Type.initTag(.noreturn),
871 .fn_naked_noreturn_no_args => Type.initTag(.noreturn),1358 .fn_naked_noreturn_no_args => Type.initTag(.noreturn),
872 .fn_ccc_void_no_args => Type.initTag(.void),1359
1360 .fn_void_no_args,
1361 .fn_ccc_void_no_args,
1362 => Type.initTag(.void),
1363
1364 .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).return_type,
8731365
874 .f16,1366 .f16,
875 .f32,1367 .f32,
...@@ -893,6 +1385,12 @@ pub const Type = extern union {...@@ -893,6 +1385,12 @@ pub const Type = extern union {
893 .const_slice_u8,1385 .const_slice_u8,
894 .u8,1386 .u8,
895 .i8,1387 .i8,
1388 .u16,
1389 .i16,
1390 .u32,
1391 .i32,
1392 .u64,
1393 .i64,
896 .usize,1394 .usize,
897 .isize,1395 .isize,
898 .c_short,1396 .c_short,
...@@ -913,8 +1411,10 @@ pub const Type = extern union {...@@ -913,8 +1411,10 @@ pub const Type = extern union {
913 pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {1411 pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
914 return switch (self.tag()) {1412 return switch (self.tag()) {
915 .fn_noreturn_no_args => .Unspecified,1413 .fn_noreturn_no_args => .Unspecified,
1414 .fn_void_no_args => .Unspecified,
916 .fn_naked_noreturn_no_args => .Naked,1415 .fn_naked_noreturn_no_args => .Naked,
917 .fn_ccc_void_no_args => .C,1416 .fn_ccc_void_no_args => .C,
1417 .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).cc,
9181418
919 .f16,1419 .f16,
920 .f32,1420 .f32,
...@@ -938,6 +1438,12 @@ pub const Type = extern union {...@@ -938,6 +1438,12 @@ pub const Type = extern union {
938 .const_slice_u8,1438 .const_slice_u8,
939 .u8,1439 .u8,
940 .i8,1440 .i8,
1441 .u16,
1442 .i16,
1443 .u32,
1444 .i32,
1445 .u64,
1446 .i64,
941 .usize,1447 .usize,
942 .isize,1448 .isize,
943 .c_short,1449 .c_short,
...@@ -958,8 +1464,10 @@ pub const Type = extern union {...@@ -958,8 +1464,10 @@ pub const Type = extern union {
958 pub fn fnIsVarArgs(self: Type) bool {1464 pub fn fnIsVarArgs(self: Type) bool {
959 return switch (self.tag()) {1465 return switch (self.tag()) {
960 .fn_noreturn_no_args => false,1466 .fn_noreturn_no_args => false,
1467 .fn_void_no_args => false,
961 .fn_naked_noreturn_no_args => false,1468 .fn_naked_noreturn_no_args => false,
962 .fn_ccc_void_no_args => false,1469 .fn_ccc_void_no_args => false,
1470 .function => false,
9631471
964 .f16,1472 .f16,
965 .f32,1473 .f32,
...@@ -983,6 +1491,12 @@ pub const Type = extern union {...@@ -983,6 +1491,12 @@ pub const Type = extern union {
983 .const_slice_u8,1491 .const_slice_u8,
984 .u8,1492 .u8,
985 .i8,1493 .i8,
1494 .u16,
1495 .i16,
1496 .u32,
1497 .i32,
1498 .u64,
1499 .i64,
986 .usize,1500 .usize,
987 .isize,1501 .isize,
988 .c_short,1502 .c_short,
...@@ -1010,6 +1524,12 @@ pub const Type = extern union {...@@ -1010,6 +1524,12 @@ pub const Type = extern union {
1010 .comptime_float,1524 .comptime_float,
1011 .u8,1525 .u8,
1012 .i8,1526 .i8,
1527 .u16,
1528 .i16,
1529 .u32,
1530 .i32,
1531 .u64,
1532 .i64,
1013 .usize,1533 .usize,
1014 .isize,1534 .isize,
1015 .c_short,1535 .c_short,
...@@ -1033,8 +1553,10 @@ pub const Type = extern union {...@@ -1033,8 +1553,10 @@ pub const Type = extern union {
1033 .@"null",1553 .@"null",
1034 .@"undefined",1554 .@"undefined",
1035 .fn_noreturn_no_args,1555 .fn_noreturn_no_args,
1556 .fn_void_no_args,
1036 .fn_naked_noreturn_no_args,1557 .fn_naked_noreturn_no_args,
1037 .fn_ccc_void_no_args,1558 .fn_ccc_void_no_args,
1559 .function,
1038 .array,1560 .array,
1039 .single_const_pointer,1561 .single_const_pointer,
1040 .single_const_pointer_to_comptime_int,1562 .single_const_pointer_to_comptime_int,
...@@ -1056,6 +1578,12 @@ pub const Type = extern union {...@@ -1056,6 +1578,12 @@ pub const Type = extern union {
1056 .comptime_float,1578 .comptime_float,
1057 .u8,1579 .u8,
1058 .i8,1580 .i8,
1581 .u16,
1582 .i16,
1583 .u32,
1584 .i32,
1585 .u64,
1586 .i64,
1059 .usize,1587 .usize,
1060 .isize,1588 .isize,
1061 .c_short,1589 .c_short,
...@@ -1070,8 +1598,10 @@ pub const Type = extern union {...@@ -1070,8 +1598,10 @@ pub const Type = extern union {
1070 .type,1598 .type,
1071 .anyerror,1599 .anyerror,
1072 .fn_noreturn_no_args,1600 .fn_noreturn_no_args,
1601 .fn_void_no_args,
1073 .fn_naked_noreturn_no_args,1602 .fn_naked_noreturn_no_args,
1074 .fn_ccc_void_no_args,1603 .fn_ccc_void_no_args,
1604 .function,
1075 .single_const_pointer_to_comptime_int,1605 .single_const_pointer_to_comptime_int,
1076 .array_u8_sentinel_0,1606 .array_u8_sentinel_0,
1077 .const_slice_u8,1607 .const_slice_u8,
...@@ -1112,6 +1642,12 @@ pub const Type = extern union {...@@ -1112,6 +1642,12 @@ pub const Type = extern union {
1112 .comptime_float,1642 .comptime_float,
1113 .u8,1643 .u8,
1114 .i8,1644 .i8,
1645 .u16,
1646 .i16,
1647 .u32,
1648 .i32,
1649 .u64,
1650 .i64,
1115 .usize,1651 .usize,
1116 .isize,1652 .isize,
1117 .c_short,1653 .c_short,
...@@ -1126,8 +1662,10 @@ pub const Type = extern union {...@@ -1126,8 +1662,10 @@ pub const Type = extern union {
1126 .type,1662 .type,
1127 .anyerror,1663 .anyerror,
1128 .fn_noreturn_no_args,1664 .fn_noreturn_no_args,
1665 .fn_void_no_args,
1129 .fn_naked_noreturn_no_args,1666 .fn_naked_noreturn_no_args,
1130 .fn_ccc_void_no_args,1667 .fn_ccc_void_no_args,
1668 .function,
1131 .single_const_pointer_to_comptime_int,1669 .single_const_pointer_to_comptime_int,
1132 .array_u8_sentinel_0,1670 .array_u8_sentinel_0,
1133 .const_slice_u8,1671 .const_slice_u8,
...@@ -1154,8 +1692,14 @@ pub const Type = extern union {...@@ -1154,8 +1692,14 @@ pub const Type = extern union {
1154 // The first section of this enum are tags that require no payload.1692 // The first section of this enum are tags that require no payload.
1155 u8,1693 u8,
1156 i8,1694 i8,
1157 isize,1695 u16,
1696 i16,
1697 u32,
1698 i32,
1699 u64,
1700 i64,
1158 usize,1701 usize,
1702 isize,
1159 c_short,1703 c_short,
1160 c_ushort,1704 c_ushort,
1161 c_int,1705 c_int,
...@@ -1180,6 +1724,7 @@ pub const Type = extern union {...@@ -1180,6 +1724,7 @@ pub const Type = extern union {
1180 @"null",1724 @"null",
1181 @"undefined",1725 @"undefined",
1182 fn_noreturn_no_args,1726 fn_noreturn_no_args,
1727 fn_void_no_args,
1183 fn_naked_noreturn_no_args,1728 fn_naked_noreturn_no_args,
1184 fn_ccc_void_no_args,1729 fn_ccc_void_no_args,
1185 single_const_pointer_to_comptime_int,1730 single_const_pointer_to_comptime_int,
...@@ -1191,6 +1736,7 @@ pub const Type = extern union {...@@ -1191,6 +1736,7 @@ pub const Type = extern union {
1191 single_const_pointer,1736 single_const_pointer,
1192 int_signed,1737 int_signed,
1193 int_unsigned,1738 int_unsigned,
1739 function,
11941740
1195 pub const last_no_payload_tag = Tag.const_slice_u8;1741 pub const last_no_payload_tag = Tag.const_slice_u8;
1196 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;1742 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
...@@ -1229,6 +1775,14 @@ pub const Type = extern union {...@@ -1229,6 +1775,14 @@ pub const Type = extern union {
12291775
1230 bits: u16,1776 bits: u16,
1231 };1777 };
1778
1779 pub const Function = struct {
1780 base: Payload = Payload{ .tag = .function },
1781
1782 param_types: []Type,
1783 return_type: Type,
1784 cc: std.builtin.CallingConvention,
1785 };
1232 };1786 };
1233};1787};
12341788
src-self-hosted/value.zig+230-30
...@@ -23,8 +23,14 @@ pub const Value = extern union {...@@ -23,8 +23,14 @@ pub const Value = extern union {
23 // The first section of this enum are tags that require no payload.23 // The first section of this enum are tags that require no payload.
24 u8_type,24 u8_type,
25 i8_type,25 i8_type,
26 isize_type,26 u16_type,
27 i16_type,
28 u32_type,
29 i32_type,
30 u64_type,
31 i64_type,
27 usize_type,32 usize_type,
33 isize_type,
28 c_short_type,34 c_short_type,
29 c_ushort_type,35 c_ushort_type,
30 c_int_type,36 c_int_type,
...@@ -49,6 +55,7 @@ pub const Value = extern union {...@@ -49,6 +55,7 @@ pub const Value = extern union {
49 null_type,55 null_type,
50 undefined_type,56 undefined_type,
51 fn_noreturn_no_args_type,57 fn_noreturn_no_args_type,
58 fn_void_no_args_type,
52 fn_naked_noreturn_no_args_type,59 fn_naked_noreturn_no_args_type,
53 fn_ccc_void_no_args_type,60 fn_ccc_void_no_args_type,
54 single_const_pointer_to_comptime_int_type,61 single_const_pointer_to_comptime_int_type,
...@@ -78,8 +85,8 @@ pub const Value = extern union {...@@ -78,8 +85,8 @@ pub const Value = extern union {
78 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;85 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
79 };86 };
8087
81 pub fn initTag(comptime small_tag: Tag) Value {88 pub fn initTag(small_tag: Tag) Value {
82 comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);89 assert(@enumToInt(small_tag) < Tag.no_payload_count);
83 return .{ .tag_if_small_enough = @enumToInt(small_tag) };90 return .{ .tag_if_small_enough = @enumToInt(small_tag) };
84 }91 }
8592
...@@ -107,17 +114,132 @@ pub const Value = extern union {...@@ -107,17 +114,132 @@ pub const Value = extern union {
107 return @fieldParentPtr(T, "base", self.ptr_otherwise);114 return @fieldParentPtr(T, "base", self.ptr_otherwise);
108 }115 }
109116
117 pub fn copy(self: Value, allocator: *Allocator) error{OutOfMemory}!Value {
118 if (self.tag_if_small_enough < Tag.no_payload_count) {
119 return Value{ .tag_if_small_enough = self.tag_if_small_enough };
120 } else switch (self.ptr_otherwise.tag) {
121 .u8_type,
122 .i8_type,
123 .u16_type,
124 .i16_type,
125 .u32_type,
126 .i32_type,
127 .u64_type,
128 .i64_type,
129 .usize_type,
130 .isize_type,
131 .c_short_type,
132 .c_ushort_type,
133 .c_int_type,
134 .c_uint_type,
135 .c_long_type,
136 .c_ulong_type,
137 .c_longlong_type,
138 .c_ulonglong_type,
139 .c_longdouble_type,
140 .f16_type,
141 .f32_type,
142 .f64_type,
143 .f128_type,
144 .c_void_type,
145 .bool_type,
146 .void_type,
147 .type_type,
148 .anyerror_type,
149 .comptime_int_type,
150 .comptime_float_type,
151 .noreturn_type,
152 .null_type,
153 .undefined_type,
154 .fn_noreturn_no_args_type,
155 .fn_void_no_args_type,
156 .fn_naked_noreturn_no_args_type,
157 .fn_ccc_void_no_args_type,
158 .single_const_pointer_to_comptime_int_type,
159 .const_slice_u8_type,
160 .undef,
161 .zero,
162 .the_one_possible_value,
163 .null_value,
164 .bool_true,
165 .bool_false,
166 => unreachable,
167
168 .ty => {
169 const payload = @fieldParentPtr(Payload.Ty, "base", self.ptr_otherwise);
170 const new_payload = try allocator.create(Payload.Ty);
171 new_payload.* = .{
172 .base = payload.base,
173 .ty = try payload.ty.copy(allocator),
174 };
175 return Value{ .ptr_otherwise = &new_payload.base };
176 },
177 .int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64),
178 .int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64),
179 .int_big_positive => {
180 @panic("TODO implement copying of big ints");
181 },
182 .int_big_negative => {
183 @panic("TODO implement copying of big ints");
184 },
185 .function => return self.copyPayloadShallow(allocator, Payload.Function),
186 .ref_val => {
187 const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
188 const new_payload = try allocator.create(Payload.RefVal);
189 new_payload.* = .{
190 .base = payload.base,
191 .val = try payload.val.copy(allocator),
192 };
193 return Value{ .ptr_otherwise = &new_payload.base };
194 },
195 .decl_ref => return self.copyPayloadShallow(allocator, Payload.DeclRef),
196 .elem_ptr => {
197 const payload = @fieldParentPtr(Payload.ElemPtr, "base", self.ptr_otherwise);
198 const new_payload = try allocator.create(Payload.ElemPtr);
199 new_payload.* = .{
200 .base = payload.base,
201 .array_ptr = try payload.array_ptr.copy(allocator),
202 .index = payload.index,
203 };
204 return Value{ .ptr_otherwise = &new_payload.base };
205 },
206 .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
207 .repeated => {
208 const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise);
209 const new_payload = try allocator.create(Payload.Repeated);
210 new_payload.* = .{
211 .base = payload.base,
212 .val = try payload.val.copy(allocator),
213 };
214 return Value{ .ptr_otherwise = &new_payload.base };
215 },
216 }
217 }
218
219 fn copyPayloadShallow(self: Value, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Value {
220 const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
221 const new_payload = try allocator.create(T);
222 new_payload.* = payload.*;
223 return Value{ .ptr_otherwise = &new_payload.base };
224 }
225
110 pub fn format(226 pub fn format(
111 self: Value,227 self: Value,
112 comptime fmt: []const u8,228 comptime fmt: []const u8,
113 options: std.fmt.FormatOptions,229 options: std.fmt.FormatOptions,
114 out_stream: var,230 out_stream: anytype,
115 ) !void {231 ) !void {
116 comptime assert(fmt.len == 0);232 comptime assert(fmt.len == 0);
117 var val = self;233 var val = self;
118 while (true) switch (val.tag()) {234 while (true) switch (val.tag()) {
119 .u8_type => return out_stream.writeAll("u8"),235 .u8_type => return out_stream.writeAll("u8"),
120 .i8_type => return out_stream.writeAll("i8"),236 .i8_type => return out_stream.writeAll("i8"),
237 .u16_type => return out_stream.writeAll("u16"),
238 .i16_type => return out_stream.writeAll("i16"),
239 .u32_type => return out_stream.writeAll("u32"),
240 .i32_type => return out_stream.writeAll("i32"),
241 .u64_type => return out_stream.writeAll("u64"),
242 .i64_type => return out_stream.writeAll("i64"),
121 .isize_type => return out_stream.writeAll("isize"),243 .isize_type => return out_stream.writeAll("isize"),
122 .usize_type => return out_stream.writeAll("usize"),244 .usize_type => return out_stream.writeAll("usize"),
123 .c_short_type => return out_stream.writeAll("c_short"),245 .c_short_type => return out_stream.writeAll("c_short"),
...@@ -144,6 +266,7 @@ pub const Value = extern union {...@@ -144,6 +266,7 @@ pub const Value = extern union {
144 .null_type => return out_stream.writeAll("@TypeOf(null)"),266 .null_type => return out_stream.writeAll("@TypeOf(null)"),
145 .undefined_type => return out_stream.writeAll("@TypeOf(undefined)"),267 .undefined_type => return out_stream.writeAll("@TypeOf(undefined)"),
146 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),268 .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
269 .fn_void_no_args_type => return out_stream.writeAll("fn() void"),
147 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),270 .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
148 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),271 .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
149 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),272 .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
...@@ -203,8 +326,14 @@ pub const Value = extern union {...@@ -203,8 +326,14 @@ pub const Value = extern union {
203326
204 .u8_type => Type.initTag(.u8),327 .u8_type => Type.initTag(.u8),
205 .i8_type => Type.initTag(.i8),328 .i8_type => Type.initTag(.i8),
206 .isize_type => Type.initTag(.isize),329 .u16_type => Type.initTag(.u16),
330 .i16_type => Type.initTag(.i16),
331 .u32_type => Type.initTag(.u32),
332 .i32_type => Type.initTag(.i32),
333 .u64_type => Type.initTag(.u64),
334 .i64_type => Type.initTag(.i64),
207 .usize_type => Type.initTag(.usize),335 .usize_type => Type.initTag(.usize),
336 .isize_type => Type.initTag(.isize),
208 .c_short_type => Type.initTag(.c_short),337 .c_short_type => Type.initTag(.c_short),
209 .c_ushort_type => Type.initTag(.c_ushort),338 .c_ushort_type => Type.initTag(.c_ushort),
210 .c_int_type => Type.initTag(.c_int),339 .c_int_type => Type.initTag(.c_int),
...@@ -229,6 +358,7 @@ pub const Value = extern union {...@@ -229,6 +358,7 @@ pub const Value = extern union {
229 .null_type => Type.initTag(.@"null"),358 .null_type => Type.initTag(.@"null"),
230 .undefined_type => Type.initTag(.@"undefined"),359 .undefined_type => Type.initTag(.@"undefined"),
231 .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),360 .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
361 .fn_void_no_args_type => Type.initTag(.fn_void_no_args),
232 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),362 .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
233 .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),363 .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
234 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),364 .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
...@@ -260,8 +390,14 @@ pub const Value = extern union {...@@ -260,8 +390,14 @@ pub const Value = extern union {
260 .ty,390 .ty,
261 .u8_type,391 .u8_type,
262 .i8_type,392 .i8_type,
263 .isize_type,393 .u16_type,
394 .i16_type,
395 .u32_type,
396 .i32_type,
397 .u64_type,
398 .i64_type,
264 .usize_type,399 .usize_type,
400 .isize_type,
265 .c_short_type,401 .c_short_type,
266 .c_ushort_type,402 .c_ushort_type,
267 .c_int_type,403 .c_int_type,
...@@ -286,12 +422,11 @@ pub const Value = extern union {...@@ -286,12 +422,11 @@ pub const Value = extern union {
286 .null_type,422 .null_type,
287 .undefined_type,423 .undefined_type,
288 .fn_noreturn_no_args_type,424 .fn_noreturn_no_args_type,
425 .fn_void_no_args_type,
289 .fn_naked_noreturn_no_args_type,426 .fn_naked_noreturn_no_args_type,
290 .fn_ccc_void_no_args_type,427 .fn_ccc_void_no_args_type,
291 .single_const_pointer_to_comptime_int_type,428 .single_const_pointer_to_comptime_int_type,
292 .const_slice_u8_type,429 .const_slice_u8_type,
293 .bool_true,
294 .bool_false,
295 .null_value,430 .null_value,
296 .function,431 .function,
297 .ref_val,432 .ref_val,
...@@ -304,8 +439,11 @@ pub const Value = extern union {...@@ -304,8 +439,11 @@ pub const Value = extern union {
304439
305 .the_one_possible_value, // An integer with one possible value is always zero.440 .the_one_possible_value, // An integer with one possible value is always zero.
306 .zero,441 .zero,
442 .bool_false,
307 => return BigIntMutable.init(&space.limbs, 0).toConst(),443 => return BigIntMutable.init(&space.limbs, 0).toConst(),
308444
445 .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(),
446
309 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),447 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
310 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),448 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
311 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),449 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
...@@ -319,8 +457,14 @@ pub const Value = extern union {...@@ -319,8 +457,14 @@ pub const Value = extern union {
319 .ty,457 .ty,
320 .u8_type,458 .u8_type,
321 .i8_type,459 .i8_type,
322 .isize_type,460 .u16_type,
461 .i16_type,
462 .u32_type,
463 .i32_type,
464 .u64_type,
465 .i64_type,
323 .usize_type,466 .usize_type,
467 .isize_type,
324 .c_short_type,468 .c_short_type,
325 .c_ushort_type,469 .c_ushort_type,
326 .c_int_type,470 .c_int_type,
...@@ -345,12 +489,11 @@ pub const Value = extern union {...@@ -345,12 +489,11 @@ pub const Value = extern union {
345 .null_type,489 .null_type,
346 .undefined_type,490 .undefined_type,
347 .fn_noreturn_no_args_type,491 .fn_noreturn_no_args_type,
492 .fn_void_no_args_type,
348 .fn_naked_noreturn_no_args_type,493 .fn_naked_noreturn_no_args_type,
349 .fn_ccc_void_no_args_type,494 .fn_ccc_void_no_args_type,
350 .single_const_pointer_to_comptime_int_type,495 .single_const_pointer_to_comptime_int_type,
351 .const_slice_u8_type,496 .const_slice_u8_type,
352 .bool_true,
353 .bool_false,
354 .null_value,497 .null_value,
355 .function,498 .function,
356 .ref_val,499 .ref_val,
...@@ -363,8 +506,11 @@ pub const Value = extern union {...@@ -363,8 +506,11 @@ pub const Value = extern union {
363506
364 .zero,507 .zero,
365 .the_one_possible_value, // an integer with one possible value is always zero508 .the_one_possible_value, // an integer with one possible value is always zero
509 .bool_false,
366 => return 0,510 => return 0,
367511
512 .bool_true => return 1,
513
368 .int_u64 => return self.cast(Payload.Int_u64).?.int,514 .int_u64 => return self.cast(Payload.Int_u64).?.int,
369 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),515 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
370 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,516 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable,
...@@ -379,8 +525,14 @@ pub const Value = extern union {...@@ -379,8 +525,14 @@ pub const Value = extern union {
379 .ty,525 .ty,
380 .u8_type,526 .u8_type,
381 .i8_type,527 .i8_type,
382 .isize_type,528 .u16_type,
529 .i16_type,
530 .u32_type,
531 .i32_type,
532 .u64_type,
533 .i64_type,
383 .usize_type,534 .usize_type,
535 .isize_type,
384 .c_short_type,536 .c_short_type,
385 .c_ushort_type,537 .c_ushort_type,
386 .c_int_type,538 .c_int_type,
...@@ -405,12 +557,11 @@ pub const Value = extern union {...@@ -405,12 +557,11 @@ pub const Value = extern union {
405 .null_type,557 .null_type,
406 .undefined_type,558 .undefined_type,
407 .fn_noreturn_no_args_type,559 .fn_noreturn_no_args_type,
560 .fn_void_no_args_type,
408 .fn_naked_noreturn_no_args_type,561 .fn_naked_noreturn_no_args_type,
409 .fn_ccc_void_no_args_type,562 .fn_ccc_void_no_args_type,
410 .single_const_pointer_to_comptime_int_type,563 .single_const_pointer_to_comptime_int_type,
411 .const_slice_u8_type,564 .const_slice_u8_type,
412 .bool_true,
413 .bool_false,
414 .null_value,565 .null_value,
415 .function,566 .function,
416 .ref_val,567 .ref_val,
...@@ -423,8 +574,11 @@ pub const Value = extern union {...@@ -423,8 +574,11 @@ pub const Value = extern union {
423574
424 .the_one_possible_value, // an integer with one possible value is always zero575 .the_one_possible_value, // an integer with one possible value is always zero
425 .zero,576 .zero,
577 .bool_false,
426 => return 0,578 => return 0,
427579
580 .bool_true => return 1,
581
428 .int_u64 => {582 .int_u64 => {
429 const x = self.cast(Payload.Int_u64).?.int;583 const x = self.cast(Payload.Int_u64).?.int;
430 if (x == 0) return 0;584 if (x == 0) return 0;
...@@ -444,8 +598,14 @@ pub const Value = extern union {...@@ -444,8 +598,14 @@ pub const Value = extern union {
444 .ty,598 .ty,
445 .u8_type,599 .u8_type,
446 .i8_type,600 .i8_type,
447 .isize_type,601 .u16_type,
602 .i16_type,
603 .u32_type,
604 .i32_type,
605 .u64_type,
606 .i64_type,
448 .usize_type,607 .usize_type,
608 .isize_type,
449 .c_short_type,609 .c_short_type,
450 .c_ushort_type,610 .c_ushort_type,
451 .c_int_type,611 .c_int_type,
...@@ -470,12 +630,11 @@ pub const Value = extern union {...@@ -470,12 +630,11 @@ pub const Value = extern union {
470 .null_type,630 .null_type,
471 .undefined_type,631 .undefined_type,
472 .fn_noreturn_no_args_type,632 .fn_noreturn_no_args_type,
633 .fn_void_no_args_type,
473 .fn_naked_noreturn_no_args_type,634 .fn_naked_noreturn_no_args_type,
474 .fn_ccc_void_no_args_type,635 .fn_ccc_void_no_args_type,
475 .single_const_pointer_to_comptime_int_type,636 .single_const_pointer_to_comptime_int_type,
476 .const_slice_u8_type,637 .const_slice_u8_type,
477 .bool_true,
478 .bool_false,
479 .null_value,638 .null_value,
480 .function,639 .function,
481 .ref_val,640 .ref_val,
...@@ -488,8 +647,18 @@ pub const Value = extern union {...@@ -488,8 +647,18 @@ pub const Value = extern union {
488 .zero,647 .zero,
489 .undef,648 .undef,
490 .the_one_possible_value, // an integer with one possible value is always zero649 .the_one_possible_value, // an integer with one possible value is always zero
650 .bool_false,
491 => return true,651 => return true,
492652
653 .bool_true => {
654 const info = ty.intInfo(target);
655 if (info.signed) {
656 return info.bits >= 2;
657 } else {
658 return info.bits >= 1;
659 }
660 },
661
493 .int_u64 => switch (ty.zigTypeTag()) {662 .int_u64 => switch (ty.zigTypeTag()) {
494 .Int => {663 .Int => {
495 const x = self.cast(Payload.Int_u64).?.int;664 const x = self.cast(Payload.Int_u64).?.int;
...@@ -538,8 +707,14 @@ pub const Value = extern union {...@@ -538,8 +707,14 @@ pub const Value = extern union {
538 .ty,707 .ty,
539 .u8_type,708 .u8_type,
540 .i8_type,709 .i8_type,
541 .isize_type,710 .u16_type,
711 .i16_type,
712 .u32_type,
713 .i32_type,
714 .u64_type,
715 .i64_type,
542 .usize_type,716 .usize_type,
717 .isize_type,
543 .c_short_type,718 .c_short_type,
544 .c_ushort_type,719 .c_ushort_type,
545 .c_int_type,720 .c_int_type,
...@@ -564,6 +739,7 @@ pub const Value = extern union {...@@ -564,6 +739,7 @@ pub const Value = extern union {
564 .null_type,739 .null_type,
565 .undefined_type,740 .undefined_type,
566 .fn_noreturn_no_args_type,741 .fn_noreturn_no_args_type,
742 .fn_void_no_args_type,
567 .fn_naked_noreturn_no_args_type,743 .fn_naked_noreturn_no_args_type,
568 .fn_ccc_void_no_args_type,744 .fn_ccc_void_no_args_type,
569 .single_const_pointer_to_comptime_int_type,745 .single_const_pointer_to_comptime_int_type,
...@@ -594,8 +770,14 @@ pub const Value = extern union {...@@ -594,8 +770,14 @@ pub const Value = extern union {
594 .ty,770 .ty,
595 .u8_type,771 .u8_type,
596 .i8_type,772 .i8_type,
597 .isize_type,773 .u16_type,
774 .i16_type,
775 .u32_type,
776 .i32_type,
777 .u64_type,
778 .i64_type,
598 .usize_type,779 .usize_type,
780 .isize_type,
599 .c_short_type,781 .c_short_type,
600 .c_ushort_type,782 .c_ushort_type,
601 .c_int_type,783 .c_int_type,
...@@ -620,12 +802,11 @@ pub const Value = extern union {...@@ -620,12 +802,11 @@ pub const Value = extern union {
620 .null_type,802 .null_type,
621 .undefined_type,803 .undefined_type,
622 .fn_noreturn_no_args_type,804 .fn_noreturn_no_args_type,
805 .fn_void_no_args_type,
623 .fn_naked_noreturn_no_args_type,806 .fn_naked_noreturn_no_args_type,
624 .fn_ccc_void_no_args_type,807 .fn_ccc_void_no_args_type,
625 .single_const_pointer_to_comptime_int_type,808 .single_const_pointer_to_comptime_int_type,
626 .const_slice_u8_type,809 .const_slice_u8_type,
627 .bool_true,
628 .bool_false,
629 .null_value,810 .null_value,
630 .function,811 .function,
631 .ref_val,812 .ref_val,
...@@ -638,8 +819,11 @@ pub const Value = extern union {...@@ -638,8 +819,11 @@ pub const Value = extern union {
638819
639 .zero,820 .zero,
640 .the_one_possible_value, // an integer with one possible value is always zero821 .the_one_possible_value, // an integer with one possible value is always zero
822 .bool_false,
641 => return .eq,823 => return .eq,
642824
825 .bool_true => return .gt,
826
643 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),827 .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0),
644 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),828 .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0),
645 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),829 .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0),
...@@ -683,7 +867,7 @@ pub const Value = extern union {...@@ -683,7 +867,7 @@ pub const Value = extern union {
683 pub fn toBool(self: Value) bool {867 pub fn toBool(self: Value) bool {
684 return switch (self.tag()) {868 return switch (self.tag()) {
685 .bool_true => true,869 .bool_true => true,
686 .bool_false => false,870 .bool_false, .zero => false,
687 else => unreachable,871 else => unreachable,
688 };872 };
689 }873 }
...@@ -695,8 +879,14 @@ pub const Value = extern union {...@@ -695,8 +879,14 @@ pub const Value = extern union {
695 .ty,879 .ty,
696 .u8_type,880 .u8_type,
697 .i8_type,881 .i8_type,
698 .isize_type,882 .u16_type,
883 .i16_type,
884 .u32_type,
885 .i32_type,
886 .u64_type,
887 .i64_type,
699 .usize_type,888 .usize_type,
889 .isize_type,
700 .c_short_type,890 .c_short_type,
701 .c_ushort_type,891 .c_ushort_type,
702 .c_int_type,892 .c_int_type,
...@@ -721,6 +911,7 @@ pub const Value = extern union {...@@ -721,6 +911,7 @@ pub const Value = extern union {
721 .null_type,911 .null_type,
722 .undefined_type,912 .undefined_type,
723 .fn_noreturn_no_args_type,913 .fn_noreturn_no_args_type,
914 .fn_void_no_args_type,
724 .fn_naked_noreturn_no_args_type,915 .fn_naked_noreturn_no_args_type,
725 .fn_ccc_void_no_args_type,916 .fn_ccc_void_no_args_type,
726 .single_const_pointer_to_comptime_int_type,917 .single_const_pointer_to_comptime_int_type,
...@@ -757,8 +948,14 @@ pub const Value = extern union {...@@ -757,8 +948,14 @@ pub const Value = extern union {
757 .ty,948 .ty,
758 .u8_type,949 .u8_type,
759 .i8_type,950 .i8_type,
760 .isize_type,951 .u16_type,
952 .i16_type,
953 .u32_type,
954 .i32_type,
955 .u64_type,
956 .i64_type,
761 .usize_type,957 .usize_type,
958 .isize_type,
762 .c_short_type,959 .c_short_type,
763 .c_ushort_type,960 .c_ushort_type,
764 .c_int_type,961 .c_int_type,
...@@ -783,6 +980,7 @@ pub const Value = extern union {...@@ -783,6 +980,7 @@ pub const Value = extern union {
783 .null_type,980 .null_type,
784 .undefined_type,981 .undefined_type,
785 .fn_noreturn_no_args_type,982 .fn_noreturn_no_args_type,
983 .fn_void_no_args_type,
786 .fn_naked_noreturn_no_args_type,984 .fn_naked_noreturn_no_args_type,
787 .fn_ccc_void_no_args_type,985 .fn_ccc_void_no_args_type,
788 .single_const_pointer_to_comptime_int_type,986 .single_const_pointer_to_comptime_int_type,
...@@ -836,8 +1034,14 @@ pub const Value = extern union {...@@ -836,8 +1034,14 @@ pub const Value = extern union {
836 .ty,1034 .ty,
837 .u8_type,1035 .u8_type,
838 .i8_type,1036 .i8_type,
839 .isize_type,1037 .u16_type,
1038 .i16_type,
1039 .u32_type,
1040 .i32_type,
1041 .u64_type,
1042 .i64_type,
840 .usize_type,1043 .usize_type,
1044 .isize_type,
841 .c_short_type,1045 .c_short_type,
842 .c_ushort_type,1046 .c_ushort_type,
843 .c_int_type,1047 .c_int_type,
...@@ -862,6 +1066,7 @@ pub const Value = extern union {...@@ -862,6 +1066,7 @@ pub const Value = extern union {
862 .null_type,1066 .null_type,
863 .undefined_type,1067 .undefined_type,
864 .fn_noreturn_no_args_type,1068 .fn_noreturn_no_args_type,
1069 .fn_void_no_args_type,
865 .fn_naked_noreturn_no_args_type,1070 .fn_naked_noreturn_no_args_type,
866 .fn_ccc_void_no_args_type,1071 .fn_ccc_void_no_args_type,
867 .single_const_pointer_to_comptime_int_type,1072 .single_const_pointer_to_comptime_int_type,
...@@ -929,11 +1134,6 @@ pub const Value = extern union {...@@ -929,11 +1134,6 @@ pub const Value = extern union {
929 len: u64,1134 len: u64,
930 };1135 };
9311136
932 pub const SingleConstPtrType = struct {
933 base: Payload = Payload{ .tag = .single_const_ptr_type },
934 elem_type: *Type,
935 };
936
937 /// Represents a pointer to another immutable value.1137 /// Represents a pointer to another immutable value.
938 pub const RefVal = struct {1138 pub const RefVal = struct {
939 base: Payload = Payload{ .tag = .ref_val },1139 base: Payload = Payload{ .tag = .ref_val },
src-self-hosted/zir.zig+661-235
...@@ -12,29 +12,56 @@ const TypedValue = @import("TypedValue.zig");...@@ -12,29 +12,56 @@ const TypedValue = @import("TypedValue.zig");
12const ir = @import("ir.zig");12const ir = @import("ir.zig");
13const IrModule = @import("Module.zig");13const IrModule = @import("Module.zig");
1414
15/// This struct is relevent only for the ZIR Module text format. It is not used for
16/// semantic analysis of Zig source code.
17pub const Decl = struct {
18 name: []const u8,
19
20 /// Hash of slice into the source of the part after the = and before the next instruction.
21 contents_hash: std.zig.SrcHash,
22
23 inst: *Inst,
24};
25
15/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for26/// These are instructions that correspond to the ZIR text format. See `ir.Inst` for
16/// in-memory, analyzed instructions with types and values.27/// in-memory, analyzed instructions with types and values.
17pub const Inst = struct {28pub const Inst = struct {
18 tag: Tag,29 tag: Tag,
19 /// Byte offset into the source.30 /// Byte offset into the source.
20 src: usize,31 src: usize,
21 name: []const u8,32 /// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions.
2233 analyzed_inst: ?*ir.Inst = null,
23 /// Slice into the source of the part after the = and before the next instruction.
24 contents: []const u8 = &[0]u8{},
2534
26 /// These names are used directly as the instruction names in the text format.35 /// These names are used directly as the instruction names in the text format.
27 pub const Tag = enum {36 pub const Tag = enum {
37 /// Function parameter value. These must be first in a function's main block,
38 /// in respective order with the parameters.
39 arg,
40 /// A labeled block of code, which can return a value.
41 block,
42 /// Return a value from a `Block`.
43 @"break",
28 breakpoint,44 breakpoint,
45 /// Same as `break` but without an operand; the operand is assumed to be the void value.
46 breakvoid,
29 call,47 call,
30 compileerror,48 compileerror,
49 /// Special case, has no textual representation.
50 @"const",
31 /// Represents a pointer to a global decl by name.51 /// Represents a pointer to a global decl by name.
32 declref,52 declref,
53 /// Represents a pointer to a global decl by string name.
54 declref_str,
33 /// The syntax `@foo` is equivalent to `declval("foo")`.55 /// The syntax `@foo` is equivalent to `declval("foo")`.
34 /// declval is equivalent to declref followed by deref.56 /// declval is equivalent to declref followed by deref.
35 declval,57 declval,
58 /// Same as declval but the parameter is a `*Module.Decl` rather than a name.
59 declval_in_module,
60 boolnot,
61 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
36 str,62 str,
37 int,63 int,
64 inttype,
38 ptrtoint,65 ptrtoint,
39 fieldptr,66 fieldptr,
40 deref,67 deref,
...@@ -42,30 +69,87 @@ pub const Inst = struct {...@@ -42,30 +69,87 @@ pub const Inst = struct {
42 @"asm",69 @"asm",
43 @"unreachable",70 @"unreachable",
44 @"return",71 @"return",
72 returnvoid,
45 @"fn",73 @"fn",
74 fntype,
46 @"export",75 @"export",
47 primitive,76 primitive,
48 ref,
49 fntype,
50 intcast,77 intcast,
51 bitcast,78 bitcast,
52 elemptr,79 elemptr,
53 add,80 add,
81 sub,
54 cmp,82 cmp,
55 condbr,83 condbr,
56 isnull,84 isnull,
57 isnonnull,85 isnonnull,
86
87 /// Returns whether the instruction is one of the control flow "noreturn" types.
88 /// Function calls do not count.
89 pub fn isNoReturn(tag: Tag) bool {
90 return switch (tag) {
91 .arg,
92 .block,
93 .breakpoint,
94 .call,
95 .@"const",
96 .declref,
97 .declref_str,
98 .declval,
99 .declval_in_module,
100 .str,
101 .int,
102 .inttype,
103 .ptrtoint,
104 .fieldptr,
105 .deref,
106 .as,
107 .@"asm",
108 .@"fn",
109 .fntype,
110 .@"export",
111 .primitive,
112 .intcast,
113 .bitcast,
114 .elemptr,
115 .add,
116 .sub,
117 .cmp,
118 .isnull,
119 .isnonnull,
120 .boolnot,
121 => false,
122
123 .condbr,
124 .@"unreachable",
125 .@"return",
126 .returnvoid,
127 .@"break",
128 .breakvoid,
129 .compileerror,
130 => true,
131 };
132 }
58 };133 };
59134
60 pub fn TagToType(tag: Tag) type {135 pub fn TagToType(tag: Tag) type {
61 return switch (tag) {136 return switch (tag) {
137 .arg => Arg,
138 .block => Block,
139 .@"break" => Break,
62 .breakpoint => Breakpoint,140 .breakpoint => Breakpoint,
141 .breakvoid => BreakVoid,
63 .call => Call,142 .call => Call,
64 .declref => DeclRef,143 .declref => DeclRef,
144 .declref_str => DeclRefStr,
65 .declval => DeclVal,145 .declval => DeclVal,
146 .declval_in_module => DeclValInModule,
66 .compileerror => CompileError,147 .compileerror => CompileError,
148 .@"const" => Const,
149 .boolnot => BoolNot,
67 .str => Str,150 .str => Str,
68 .int => Int,151 .int => Int,
152 .inttype => IntType,
69 .ptrtoint => PtrToInt,153 .ptrtoint => PtrToInt,
70 .fieldptr => FieldPtr,154 .fieldptr => FieldPtr,
71 .deref => Deref,155 .deref => Deref,
...@@ -73,15 +157,16 @@ pub const Inst = struct {...@@ -73,15 +157,16 @@ pub const Inst = struct {
73 .@"asm" => Asm,157 .@"asm" => Asm,
74 .@"unreachable" => Unreachable,158 .@"unreachable" => Unreachable,
75 .@"return" => Return,159 .@"return" => Return,
160 .returnvoid => ReturnVoid,
76 .@"fn" => Fn,161 .@"fn" => Fn,
77 .@"export" => Export,162 .@"export" => Export,
78 .primitive => Primitive,163 .primitive => Primitive,
79 .ref => Ref,
80 .fntype => FnType,164 .fntype => FnType,
81 .intcast => IntCast,165 .intcast => IntCast,
82 .bitcast => BitCast,166 .bitcast => BitCast,
83 .elemptr => ElemPtr,167 .elemptr => ElemPtr,
84 .add => Add,168 .add => Add,
169 .sub => Sub,
85 .cmp => Cmp,170 .cmp => Cmp,
86 .condbr => CondBr,171 .condbr => CondBr,
87 .isnull => IsNull,172 .isnull => IsNull,
...@@ -96,6 +181,35 @@ pub const Inst = struct {...@@ -96,6 +181,35 @@ pub const Inst = struct {
96 return @fieldParentPtr(T, "base", base);181 return @fieldParentPtr(T, "base", base);
97 }182 }
98183
184 pub const Arg = struct {
185 pub const base_tag = Tag.arg;
186 base: Inst,
187
188 positionals: struct {},
189 kw_args: struct {},
190 };
191
192 pub const Block = struct {
193 pub const base_tag = Tag.block;
194 base: Inst,
195
196 positionals: struct {
197 body: Module.Body,
198 },
199 kw_args: struct {},
200 };
201
202 pub const Break = struct {
203 pub const base_tag = Tag.@"break";
204 base: Inst,
205
206 positionals: struct {
207 block: *Block,
208 operand: *Inst,
209 },
210 kw_args: struct {},
211 };
212
99 pub const Breakpoint = struct {213 pub const Breakpoint = struct {
100 pub const base_tag = Tag.breakpoint;214 pub const base_tag = Tag.breakpoint;
101 base: Inst,215 base: Inst,
...@@ -104,6 +218,16 @@ pub const Inst = struct {...@@ -104,6 +218,16 @@ pub const Inst = struct {
104 kw_args: struct {},218 kw_args: struct {},
105 };219 };
106220
221 pub const BreakVoid = struct {
222 pub const base_tag = Tag.breakvoid;
223 base: Inst,
224
225 positionals: struct {
226 block: *Block,
227 },
228 kw_args: struct {},
229 };
230
107 pub const Call = struct {231 pub const Call = struct {
108 pub const base_tag = Tag.call;232 pub const base_tag = Tag.call;
109 base: Inst,233 base: Inst,
...@@ -121,6 +245,16 @@ pub const Inst = struct {...@@ -121,6 +245,16 @@ pub const Inst = struct {
121 pub const base_tag = Tag.declref;245 pub const base_tag = Tag.declref;
122 base: Inst,246 base: Inst,
123247
248 positionals: struct {
249 name: []const u8,
250 },
251 kw_args: struct {},
252 };
253
254 pub const DeclRefStr = struct {
255 pub const base_tag = Tag.declref_str;
256 base: Inst,
257
124 positionals: struct {258 positionals: struct {
125 name: *Inst,259 name: *Inst,
126 },260 },
...@@ -137,6 +271,16 @@ pub const Inst = struct {...@@ -137,6 +271,16 @@ pub const Inst = struct {
137 kw_args: struct {},271 kw_args: struct {},
138 };272 };
139273
274 pub const DeclValInModule = struct {
275 pub const base_tag = Tag.declval_in_module;
276 base: Inst,
277
278 positionals: struct {
279 decl: *IrModule.Decl,
280 },
281 kw_args: struct {},
282 };
283
140 pub const CompileError = struct {284 pub const CompileError = struct {
141 pub const base_tag = Tag.compileerror;285 pub const base_tag = Tag.compileerror;
142 base: Inst,286 base: Inst,
...@@ -147,6 +291,26 @@ pub const Inst = struct {...@@ -147,6 +291,26 @@ pub const Inst = struct {
147 kw_args: struct {},291 kw_args: struct {},
148 };292 };
149293
294 pub const Const = struct {
295 pub const base_tag = Tag.@"const";
296 base: Inst,
297
298 positionals: struct {
299 typed_value: TypedValue,
300 },
301 kw_args: struct {},
302 };
303
304 pub const BoolNot = struct {
305 pub const base_tag = Tag.boolnot;
306 base: Inst,
307
308 positionals: struct {
309 operand: *Inst,
310 },
311 kw_args: struct {},
312 };
313
150 pub const Str = struct {314 pub const Str = struct {
151 pub const base_tag = Tag.str;315 pub const base_tag = Tag.str;
152 base: Inst,316 base: Inst,
...@@ -168,6 +332,7 @@ pub const Inst = struct {...@@ -168,6 +332,7 @@ pub const Inst = struct {
168 };332 };
169333
170 pub const PtrToInt = struct {334 pub const PtrToInt = struct {
335 pub const builtin_name = "@ptrToInt";
171 pub const base_tag = Tag.ptrtoint;336 pub const base_tag = Tag.ptrtoint;
172 base: Inst,337 base: Inst,
173338
...@@ -200,6 +365,7 @@ pub const Inst = struct {...@@ -200,6 +365,7 @@ pub const Inst = struct {
200365
201 pub const As = struct {366 pub const As = struct {
202 pub const base_tag = Tag.as;367 pub const base_tag = Tag.as;
368 pub const builtin_name = "@as";
203 base: Inst,369 base: Inst,
204370
205 positionals: struct {371 positionals: struct {
...@@ -238,6 +404,16 @@ pub const Inst = struct {...@@ -238,6 +404,16 @@ pub const Inst = struct {
238 pub const base_tag = Tag.@"return";404 pub const base_tag = Tag.@"return";
239 base: Inst,405 base: Inst,
240406
407 positionals: struct {
408 operand: *Inst,
409 },
410 kw_args: struct {},
411 };
412
413 pub const ReturnVoid = struct {
414 pub const base_tag = Tag.returnvoid;
415 base: Inst,
416
241 positionals: struct {},417 positionals: struct {},
242 kw_args: struct {},418 kw_args: struct {},
243 };419 };
...@@ -253,23 +429,37 @@ pub const Inst = struct {...@@ -253,23 +429,37 @@ pub const Inst = struct {
253 kw_args: struct {},429 kw_args: struct {},
254 };430 };
255431
256 pub const Export = struct {432 pub const FnType = struct {
257 pub const base_tag = Tag.@"export";433 pub const base_tag = Tag.fntype;
258 base: Inst,434 base: Inst,
259435
260 positionals: struct {436 positionals: struct {
261 symbol_name: *Inst,437 param_types: []*Inst,
262 value: *Inst,438 return_type: *Inst,
439 },
440 kw_args: struct {
441 cc: std.builtin.CallingConvention = .Unspecified,
442 },
443 };
444
445 pub const IntType = struct {
446 pub const base_tag = Tag.inttype;
447 base: Inst,
448
449 positionals: struct {
450 signed: *Inst,
451 bits: *Inst,
263 },452 },
264 kw_args: struct {},453 kw_args: struct {},
265 };454 };
266455
267 pub const Ref = struct {456 pub const Export = struct {
268 pub const base_tag = Tag.ref;457 pub const base_tag = Tag.@"export";
269 base: Inst,458 base: Inst,
270459
271 positionals: struct {460 positionals: struct {
272 operand: *Inst,461 symbol_name: *Inst,
462 decl_name: []const u8,
273 },463 },
274 kw_args: struct {},464 kw_args: struct {},
275 };465 };
...@@ -284,6 +474,14 @@ pub const Inst = struct {...@@ -284,6 +474,14 @@ pub const Inst = struct {
284 kw_args: struct {},474 kw_args: struct {},
285475
286 pub const Builtin = enum {476 pub const Builtin = enum {
477 i8,
478 u8,
479 i16,
480 u16,
481 i32,
482 u32,
483 i64,
484 u64,
287 isize,485 isize,
288 usize,486 usize,
289 c_short,487 c_short,
...@@ -315,6 +513,14 @@ pub const Inst = struct {...@@ -315,6 +513,14 @@ pub const Inst = struct {
315513
316 pub fn toTypedValue(self: Builtin) TypedValue {514 pub fn toTypedValue(self: Builtin) TypedValue {
317 return switch (self) {515 return switch (self) {
516 .i8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i8_type) },
517 .u8 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u8_type) },
518 .i16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i16_type) },
519 .u16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u16_type) },
520 .i32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i32_type) },
521 .u32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u32_type) },
522 .i64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.i64_type) },
523 .u64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.u64_type) },
318 .isize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.isize_type) },524 .isize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.isize_type) },
319 .usize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type) },525 .usize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type) },
320 .c_short => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_short_type) },526 .c_short => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_short_type) },
...@@ -348,19 +554,6 @@ pub const Inst = struct {...@@ -348,19 +554,6 @@ pub const Inst = struct {
348 };554 };
349 };555 };
350556
351 pub const FnType = struct {
352 pub const base_tag = Tag.fntype;
353 base: Inst,
354
355 positionals: struct {
356 param_types: []*Inst,
357 return_type: *Inst,
358 },
359 kw_args: struct {
360 cc: std.builtin.CallingConvention = .Unspecified,
361 },
362 };
363
364 pub const IntCast = struct {557 pub const IntCast = struct {
365 pub const base_tag = Tag.intcast;558 pub const base_tag = Tag.intcast;
366 base: Inst,559 base: Inst,
...@@ -405,6 +598,19 @@ pub const Inst = struct {...@@ -405,6 +598,19 @@ pub const Inst = struct {
405 kw_args: struct {},598 kw_args: struct {},
406 };599 };
407600
601 pub const Sub = struct {
602 pub const base_tag = Tag.sub;
603 base: Inst,
604
605 positionals: struct {
606 lhs: *Inst,
607 rhs: *Inst,
608 },
609 kw_args: struct {},
610 };
611
612 /// TODO get rid of the op positional arg and make that data part of
613 /// the base Inst tag.
408 pub const Cmp = struct {614 pub const Cmp = struct {
409 pub const base_tag = Tag.cmp;615 pub const base_tag = Tag.cmp;
410 base: Inst,616 base: Inst,
...@@ -456,7 +662,7 @@ pub const ErrorMsg = struct {...@@ -456,7 +662,7 @@ pub const ErrorMsg = struct {
456};662};
457663
458pub const Module = struct {664pub const Module = struct {
459 decls: []*Inst,665 decls: []*Decl,
460 arena: std.heap.ArenaAllocator,666 arena: std.heap.ArenaAllocator,
461 error_msg: ?ErrorMsg = null,667 error_msg: ?ErrorMsg = null,
462668
...@@ -475,13 +681,31 @@ pub const Module = struct {...@@ -475,13 +681,31 @@ pub const Module = struct {
475 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};681 self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {};
476 }682 }
477683
478 const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize });684 const DeclAndIndex = struct {
685 decl: *Decl,
686 index: usize,
687 };
479688
480 /// TODO Look into making a table to speed this up.689 /// TODO Look into making a table to speed this up.
481 pub fn findDecl(self: Module, name: []const u8) ?*Inst {690 pub fn findDecl(self: Module, name: []const u8) ?DeclAndIndex {
482 for (self.decls) |decl| {691 for (self.decls) |decl, i| {
483 if (mem.eql(u8, decl.name, name)) {692 if (mem.eql(u8, decl.name, name)) {
484 return decl;693 return DeclAndIndex{
694 .decl = decl,
695 .index = i,
696 };
697 }
698 }
699 return null;
700 }
701
702 pub fn findInstDecl(self: Module, inst: *Inst) ?DeclAndIndex {
703 for (self.decls) |decl, i| {
704 if (decl.inst == inst) {
705 return DeclAndIndex{
706 .decl = decl,
707 .index = i,
708 };
485 }709 }
486 }710 }
487 return null;711 return null;
...@@ -489,75 +713,68 @@ pub const Module = struct {...@@ -489,75 +713,68 @@ pub const Module = struct {
489713
490 /// The allocator is used for temporary storage, but this function always returns714 /// The allocator is used for temporary storage, but this function always returns
491 /// with no resources allocated.715 /// with no resources allocated.
492 pub fn writeToStream(self: Module, allocator: *Allocator, stream: var) !void {716 pub fn writeToStream(self: Module, allocator: *Allocator, stream: anytype) !void {
493 // First, build a map of *Inst to @ or % indexes717 var write = Writer{
494 var inst_table = InstPtrTable.init(allocator);718 .module = &self,
495 defer inst_table.deinit();719 .inst_table = InstPtrTable.init(allocator),
720 .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),
721 .arena = std.heap.ArenaAllocator.init(allocator),
722 .indent = 2,
723 };
724 defer write.arena.deinit();
725 defer write.inst_table.deinit();
726 defer write.block_table.deinit();
496727
497 try inst_table.ensureCapacity(self.decls.len);728 // First, build a map of *Inst to @ or % indexes
729 try write.inst_table.ensureCapacity(self.decls.len);
498730
499 for (self.decls) |decl, decl_i| {731 for (self.decls) |decl, decl_i| {
500 try inst_table.putNoClobber(decl, .{ .inst = decl, .index = null });732 try write.inst_table.putNoClobber(decl.inst, .{ .inst = decl.inst, .index = null, .name = decl.name });
501733
502 if (decl.cast(Inst.Fn)) |fn_inst| {734 if (decl.inst.cast(Inst.Fn)) |fn_inst| {
503 for (fn_inst.positionals.body.instructions) |inst, inst_i| {735 for (fn_inst.positionals.body.instructions) |inst, inst_i| {
504 try inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i });736 try write.inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i, .name = undefined });
505 }737 }
506 }738 }
507 }739 }
508740
509 for (self.decls) |decl, i| {741 for (self.decls) |decl, i| {
510 try stream.print("@{} ", .{decl.name});742 try stream.print("@{} ", .{decl.name});
511 try self.writeInstToStream(stream, decl, &inst_table);743 try write.writeInstToStream(stream, decl.inst);
512 try stream.writeByte('\n');744 try stream.writeByte('\n');
513 }745 }
514 }746 }
747};
748
749const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize, name: []const u8 });
750
751const Writer = struct {
752 module: *const Module,
753 inst_table: InstPtrTable,
754 block_table: std.AutoHashMap(*Inst.Block, []const u8),
755 arena: std.heap.ArenaAllocator,
756 indent: usize,
515757
516 fn writeInstToStream(758 fn writeInstToStream(
517 self: Module,759 self: *Writer,
518 stream: var,760 stream: anytype,
519 decl: *Inst,761 inst: *Inst,
520 inst_table: *const InstPtrTable,762 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
521 ) @TypeOf(stream).Error!void {763 inline for (@typeInfo(Inst.Tag).Enum.fields) |enum_field| {
522 // TODO I tried implementing this with an inline for loop and hit a compiler bug764 const expected_tag = @field(Inst.Tag, enum_field.name);
523 switch (decl.tag) {765 if (inst.tag == expected_tag) {
524 .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table),766 return self.writeInstToStreamGeneric(stream, expected_tag, inst);
525 .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table),767 }
526 .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table),
527 .declval => return self.writeInstToStreamGeneric(stream, .declval, decl, inst_table),
528 .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, decl, inst_table),
529 .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
530 .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
531 .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
532 .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table),
533 .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table),
534 .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table),
535 .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table),
536 .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table),
537 .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table),
538 .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table),
539 .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table),
540 .ref => return self.writeInstToStreamGeneric(stream, .ref, decl, inst_table),
541 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
542 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
543 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
544 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
545 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table),
546 .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table),
547 .cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table),
548 .condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table),
549 .isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table),
550 .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table),
551 }768 }
769 unreachable; // all tags handled
552 }770 }
553771
554 fn writeInstToStreamGeneric(772 fn writeInstToStreamGeneric(
555 self: Module,773 self: *Writer,
556 stream: var,774 stream: anytype,
557 comptime inst_tag: Inst.Tag,775 comptime inst_tag: Inst.Tag,
558 base: *Inst,776 base: *Inst,
559 inst_table: *const InstPtrTable,777 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
560 ) !void {
561 const SpecificInst = Inst.TagToType(inst_tag);778 const SpecificInst = Inst.TagToType(inst_tag);
562 const inst = @fieldParentPtr(SpecificInst, "base", base);779 const inst = @fieldParentPtr(SpecificInst, "base", base);
563 const Positionals = @TypeOf(inst.positionals);780 const Positionals = @TypeOf(inst.positionals);
...@@ -567,7 +784,7 @@ pub const Module = struct {...@@ -567,7 +784,7 @@ pub const Module = struct {
567 if (i != 0) {784 if (i != 0) {
568 try stream.writeAll(", ");785 try stream.writeAll(", ");
569 }786 }
570 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name), inst_table);787 try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name));
571 }788 }
572789
573 comptime var need_comma = pos_fields.len != 0;790 comptime var need_comma = pos_fields.len != 0;
...@@ -577,13 +794,13 @@ pub const Module = struct {...@@ -577,13 +794,13 @@ pub const Module = struct {
577 if (@field(inst.kw_args, arg_field.name)) |non_optional| {794 if (@field(inst.kw_args, arg_field.name)) |non_optional| {
578 if (need_comma) try stream.writeAll(", ");795 if (need_comma) try stream.writeAll(", ");
579 try stream.print("{}=", .{arg_field.name});796 try stream.print("{}=", .{arg_field.name});
580 try self.writeParamToStream(stream, non_optional, inst_table);797 try self.writeParamToStream(stream, non_optional);
581 need_comma = true;798 need_comma = true;
582 }799 }
583 } else {800 } else {
584 if (need_comma) try stream.writeAll(", ");801 if (need_comma) try stream.writeAll(", ");
585 try stream.print("{}=", .{arg_field.name});802 try stream.print("{}=", .{arg_field.name});
586 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name), inst_table);803 try self.writeParamToStream(stream, @field(inst.kw_args, arg_field.name));
587 need_comma = true;804 need_comma = true;
588 }805 }
589 }806 }
...@@ -591,56 +808,73 @@ pub const Module = struct {...@@ -591,56 +808,73 @@ pub const Module = struct {
591 try stream.writeByte(')');808 try stream.writeByte(')');
592 }809 }
593810
594 fn writeParamToStream(self: Module, stream: var, param: var, inst_table: *const InstPtrTable) !void {811 fn writeParamToStream(self: *Writer, stream: anytype, param: anytype) !void {
595 if (@typeInfo(@TypeOf(param)) == .Enum) {812 if (@typeInfo(@TypeOf(param)) == .Enum) {
596 return stream.writeAll(@tagName(param));813 return stream.writeAll(@tagName(param));
597 }814 }
598 switch (@TypeOf(param)) {815 switch (@TypeOf(param)) {
599 *Inst => return self.writeInstParamToStream(stream, param, inst_table),816 *Inst => return self.writeInstParamToStream(stream, param),
600 []*Inst => {817 []*Inst => {
601 try stream.writeByte('[');818 try stream.writeByte('[');
602 for (param) |inst, i| {819 for (param) |inst, i| {
603 if (i != 0) {820 if (i != 0) {
604 try stream.writeAll(", ");821 try stream.writeAll(", ");
605 }822 }
606 try self.writeInstParamToStream(stream, inst, inst_table);823 try self.writeInstParamToStream(stream, inst);
607 }824 }
608 try stream.writeByte(']');825 try stream.writeByte(']');
609 },826 },
610 Module.Body => {827 Module.Body => {
611 try stream.writeAll("{\n");828 try stream.writeAll("{\n");
612 for (param.instructions) |inst, i| {829 for (param.instructions) |inst, i| {
613 try stream.print(" %{} ", .{i});830 try stream.writeByteNTimes(' ', self.indent);
614 try self.writeInstToStream(stream, inst, inst_table);831 try stream.print("%{} ", .{i});
832 if (inst.cast(Inst.Block)) |block| {
833 const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{}", .{i});
834 try self.block_table.put(block, name);
835 }
836 self.indent += 2;
837 try self.writeInstToStream(stream, inst);
838 self.indent -= 2;
615 try stream.writeByte('\n');839 try stream.writeByte('\n');
616 }840 }
841 try stream.writeByteNTimes(' ', self.indent - 2);
617 try stream.writeByte('}');842 try stream.writeByte('}');
618 },843 },
619 bool => return stream.writeByte("01"[@boolToInt(param)]),844 bool => return stream.writeByte("01"[@boolToInt(param)]),
620 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),845 []u8, []const u8 => return std.zig.renderStringLiteral(param, stream),
621 BigIntConst => return stream.print("{}", .{param}),846 BigIntConst, usize => return stream.print("{}", .{param}),
847 TypedValue => unreachable, // this is a special case
848 *IrModule.Decl => unreachable, // this is a special case
849 *Inst.Block => {
850 const name = self.block_table.get(param).?;
851 return std.zig.renderStringLiteral(name, stream);
852 },
622 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),853 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
623 }854 }
624 }855 }
625856
626 fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void {857 fn writeInstParamToStream(self: *Writer, stream: anytype, inst: *Inst) !void {
627 if (inst_table.getValue(inst)) |info| {858 if (self.inst_table.get(inst)) |info| {
628 if (info.index) |i| {859 if (info.index) |i| {
629 try stream.print("%{}", .{info.index});860 try stream.print("%{}", .{info.index});
630 } else {861 } else {
631 try stream.print("@{}", .{info.inst.name});862 try stream.print("@{}", .{info.name});
632 }863 }
633 } else if (inst.cast(Inst.DeclVal)) |decl_val| {864 } else if (inst.cast(Inst.DeclVal)) |decl_val| {
634 try stream.print("@{}", .{decl_val.positionals.name});865 try stream.print("@{}", .{decl_val.positionals.name});
866 } else if (inst.cast(Inst.DeclValInModule)) |decl_val| {
867 try stream.print("@{}", .{decl_val.positionals.decl.name});
635 } else {868 } else {
636 //try stream.print("?", .{});869 // This should be unreachable in theory, but since ZIR is used for debugging the compiler
637 unreachable;870 // we output some debug text instead.
871 try stream.print("?{}?", .{@tagName(inst.tag)});
638 }872 }
639 }873 }
640};874};
641875
642pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module {876pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module {
643 var global_name_map = std.StringHashMap(usize).init(allocator);877 var global_name_map = std.StringHashMap(*Inst).init(allocator);
644 defer global_name_map.deinit();878 defer global_name_map.deinit();
645879
646 var parser: Parser = .{880 var parser: Parser = .{
...@@ -651,7 +885,9 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module...@@ -651,7 +885,9 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module
651 .global_name_map = &global_name_map,885 .global_name_map = &global_name_map,
652 .decls = .{},886 .decls = .{},
653 .unnamed_index = 0,887 .unnamed_index = 0,
888 .block_table = std.StringHashMap(*Inst.Block).init(allocator),
654 };889 };
890 defer parser.block_table.deinit();
655 errdefer parser.arena.deinit();891 errdefer parser.arena.deinit();
656892
657 parser.parseRoot() catch |err| switch (err) {893 parser.parseRoot() catch |err| switch (err) {
...@@ -673,23 +909,26 @@ const Parser = struct {...@@ -673,23 +909,26 @@ const Parser = struct {
673 arena: std.heap.ArenaAllocator,909 arena: std.heap.ArenaAllocator,
674 i: usize,910 i: usize,
675 source: [:0]const u8,911 source: [:0]const u8,
676 decls: std.ArrayListUnmanaged(*Inst),912 decls: std.ArrayListUnmanaged(*Decl),
677 global_name_map: *std.StringHashMap(usize),913 global_name_map: *std.StringHashMap(*Inst),
678 error_msg: ?ErrorMsg = null,914 error_msg: ?ErrorMsg = null,
679 unnamed_index: usize,915 unnamed_index: usize,
916 block_table: std.StringHashMap(*Inst.Block),
680917
681 const Body = struct {918 const Body = struct {
682 instructions: std.ArrayList(*Inst),919 instructions: std.ArrayList(*Inst),
683 name_map: std.StringHashMap(usize),920 name_map: *std.StringHashMap(*Inst),
684 };921 };
685922
686 fn parseBody(self: *Parser) !Module.Body {923 fn parseBody(self: *Parser, body_ctx: ?*Body) !Module.Body {
924 var name_map = std.StringHashMap(*Inst).init(self.allocator);
925 defer name_map.deinit();
926
687 var body_context = Body{927 var body_context = Body{
688 .instructions = std.ArrayList(*Inst).init(self.allocator),928 .instructions = std.ArrayList(*Inst).init(self.allocator),
689 .name_map = std.StringHashMap(usize).init(self.allocator),929 .name_map = if (body_ctx) |bctx| bctx.name_map else &name_map,
690 };930 };
691 defer body_context.instructions.deinit();931 defer body_context.instructions.deinit();
692 defer body_context.name_map.deinit();
693932
694 try requireEatBytes(self, "{");933 try requireEatBytes(self, "{");
695 skipSpace(self);934 skipSpace(self);
...@@ -702,12 +941,12 @@ const Parser = struct {...@@ -702,12 +941,12 @@ const Parser = struct {
702 skipSpace(self);941 skipSpace(self);
703 try requireEatBytes(self, "=");942 try requireEatBytes(self, "=");
704 skipSpace(self);943 skipSpace(self);
705 const inst = try parseInstruction(self, &body_context, ident);944 const decl = try parseInstruction(self, &body_context, ident);
706 const ident_index = body_context.instructions.items.len;945 const ident_index = body_context.instructions.items.len;
707 if (try body_context.name_map.put(ident, ident_index)) |_| {946 if (try body_context.name_map.fetchPut(ident, decl.inst)) |_| {
708 return self.fail("redefinition of identifier '{}'", .{ident});947 return self.fail("redefinition of identifier '{}'", .{ident});
709 }948 }
710 try body_context.instructions.append(inst);949 try body_context.instructions.append(decl.inst);
711 continue;950 continue;
712 },951 },
713 ' ', '\n' => continue,952 ' ', '\n' => continue,
...@@ -788,12 +1027,12 @@ const Parser = struct {...@@ -788,12 +1027,12 @@ const Parser = struct {
788 skipSpace(self);1027 skipSpace(self);
789 try requireEatBytes(self, "=");1028 try requireEatBytes(self, "=");
790 skipSpace(self);1029 skipSpace(self);
791 const inst = try parseInstruction(self, null, ident);1030 const decl = try parseInstruction(self, null, ident);
792 const ident_index = self.decls.items.len;1031 const ident_index = self.decls.items.len;
793 if (try self.global_name_map.put(ident, ident_index)) |_| {1032 if (try self.global_name_map.fetchPut(ident, decl.inst)) |_| {
794 return self.fail("redefinition of identifier '{}'", .{ident});1033 return self.fail("redefinition of identifier '{}'", .{ident});
795 }1034 }
796 try self.decls.append(self.allocator, inst);1035 try self.decls.append(self.allocator, decl);
797 },1036 },
798 ' ', '\n' => self.i += 1,1037 ' ', '\n' => self.i += 1,
799 0 => break,1038 0 => break,
...@@ -848,7 +1087,7 @@ const Parser = struct {...@@ -848,7 +1087,7 @@ const Parser = struct {
848 }1087 }
849 }1088 }
8501089
851 fn fail(self: *Parser, comptime format: []const u8, args: var) InnerError {1090 fn fail(self: *Parser, comptime format: []const u8, args: anytype) InnerError {
852 @setCold(true);1091 @setCold(true);
853 self.error_msg = ErrorMsg{1092 self.error_msg = ErrorMsg{
854 .byte_offset = self.i,1093 .byte_offset = self.i,
...@@ -857,7 +1096,7 @@ const Parser = struct {...@@ -857,7 +1096,7 @@ const Parser = struct {
857 return error.ParseFailure;1096 return error.ParseFailure;
858 }1097 }
8591098
860 fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Inst {1099 fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Decl {
861 const contents_start = self.i;1100 const contents_start = self.i;
862 const fn_name = try skipToAndOver(self, '(');1101 const fn_name = try skipToAndOver(self, '(');
863 inline for (@typeInfo(Inst.Tag).Enum.fields) |field| {1102 inline for (@typeInfo(Inst.Tag).Enum.fields) |field| {
...@@ -876,14 +1115,17 @@ const Parser = struct {...@@ -876,14 +1115,17 @@ const Parser = struct {
876 body_ctx: ?*Body,1115 body_ctx: ?*Body,
877 inst_name: []const u8,1116 inst_name: []const u8,
878 contents_start: usize,1117 contents_start: usize,
879 ) InnerError!*Inst {1118 ) InnerError!*Decl {
880 const inst_specific = try self.arena.allocator.create(InstType);1119 const inst_specific = try self.arena.allocator.create(InstType);
881 inst_specific.base = .{1120 inst_specific.base = .{
882 .name = inst_name,
883 .src = self.i,1121 .src = self.i,
884 .tag = InstType.base_tag,1122 .tag = InstType.base_tag,
885 };1123 };
8861124
1125 if (InstType == Inst.Block) {
1126 try self.block_table.put(inst_name, inst_specific);
1127 }
1128
887 if (@hasField(InstType, "ty")) {1129 if (@hasField(InstType, "ty")) {
888 inst_specific.ty = opt_type orelse {1130 inst_specific.ty = opt_type orelse {
889 return self.fail("instruction '" ++ fn_name ++ "' requires type", .{});1131 return self.fail("instruction '" ++ fn_name ++ "' requires type", .{});
...@@ -929,10 +1171,15 @@ const Parser = struct {...@@ -929,10 +1171,15 @@ const Parser = struct {
929 }1171 }
930 try requireEatBytes(self, ")");1172 try requireEatBytes(self, ")");
9311173
932 inst_specific.base.contents = self.source[contents_start..self.i];1174 const decl = try self.arena.allocator.create(Decl);
1175 decl.* = .{
1176 .name = inst_name,
1177 .contents_hash = std.zig.hashSrc(self.source[contents_start..self.i]),
1178 .inst = &inst_specific.base,
1179 };
933 //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents });1180 //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents });
9341181
935 return &inst_specific.base;1182 return decl;
936 }1183 }
9371184
938 fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T {1185 fn parseParameterGeneric(self: *Parser, comptime T: type, body_ctx: ?*Body) !T {
...@@ -950,7 +1197,7 @@ const Parser = struct {...@@ -950,7 +1197,7 @@ const Parser = struct {
950 };1197 };
951 }1198 }
952 switch (T) {1199 switch (T) {
953 Module.Body => return parseBody(self),1200 Module.Body => return parseBody(self, body_ctx),
954 bool => {1201 bool => {
955 const bool_value = switch (self.source[self.i]) {1202 const bool_value = switch (self.source[self.i]) {
956 '0' => false,1203 '0' => false,
...@@ -978,6 +1225,16 @@ const Parser = struct {...@@ -978,6 +1225,16 @@ const Parser = struct {
978 *Inst => return parseParameterInst(self, body_ctx),1225 *Inst => return parseParameterInst(self, body_ctx),
979 []u8, []const u8 => return self.parseStringLiteral(),1226 []u8, []const u8 => return self.parseStringLiteral(),
980 BigIntConst => return self.parseIntegerLiteral(),1227 BigIntConst => return self.parseIntegerLiteral(),
1228 usize => {
1229 const big_int = try self.parseIntegerLiteral();
1230 return big_int.to(usize) catch |err| return self.fail("integer literal: {}", .{@errorName(err)});
1231 },
1232 TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}),
1233 *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}),
1234 *Inst.Block => {
1235 const name = try self.parseStringLiteral();
1236 return self.block_table.get(name).?;
1237 },
981 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),1238 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
982 }1239 }
983 return self.fail("TODO parse parameter {}", .{@typeName(T)});1240 return self.fail("TODO parse parameter {}", .{@typeName(T)});
...@@ -991,7 +1248,7 @@ const Parser = struct {...@@ -991,7 +1248,7 @@ const Parser = struct {
991 };1248 };
992 const map = if (local_ref)1249 const map = if (local_ref)
993 if (body_ctx) |bc|1250 if (body_ctx) |bc|
994 &bc.name_map1251 bc.name_map
995 else1252 else
996 return self.fail("referencing a % instruction in global scope", .{})1253 return self.fail("referencing a % instruction in global scope", .{})
997 else1254 else
...@@ -1004,7 +1261,7 @@ const Parser = struct {...@@ -1004,7 +1261,7 @@ const Parser = struct {
1004 else => continue,1261 else => continue,
1005 };1262 };
1006 const ident = self.source[name_start..self.i];1263 const ident = self.source[name_start..self.i];
1007 const kv = map.get(ident) orelse {1264 return map.get(ident) orelse {
1008 const bad_name = self.source[name_start - 1 .. self.i];1265 const bad_name = self.source[name_start - 1 .. self.i];
1009 const src = name_start - 1;1266 const src = name_start - 1;
1010 if (local_ref) {1267 if (local_ref) {
...@@ -1014,7 +1271,6 @@ const Parser = struct {...@@ -1014,7 +1271,6 @@ const Parser = struct {
1014 const declval = try self.arena.allocator.create(Inst.DeclVal);1271 const declval = try self.arena.allocator.create(Inst.DeclVal);
1015 declval.* = .{1272 declval.* = .{
1016 .base = .{1273 .base = .{
1017 .name = try self.generateName(),
1018 .src = src,1274 .src = src,
1019 .tag = Inst.DeclVal.base_tag,1275 .tag = Inst.DeclVal.base_tag,
1020 },1276 },
...@@ -1024,11 +1280,6 @@ const Parser = struct {...@@ -1024,11 +1280,6 @@ const Parser = struct {
1024 return &declval.base;1280 return &declval.base;
1025 }1281 }
1026 };1282 };
1027 if (local_ref) {
1028 return body_ctx.?.instructions.items[kv.value];
1029 } else {
1030 return self.decls.items[kv.value];
1031 }
1032 }1283 }
10331284
1034 fn generateName(self: *Parser) ![]u8 {1285 fn generateName(self: *Parser) ![]u8 {
...@@ -1046,8 +1297,11 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {...@@ -1046,8 +1297,11 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module {
1046 .old_module = &old_module,1297 .old_module = &old_module,
1047 .next_auto_name = 0,1298 .next_auto_name = 0,
1048 .names = std.StringHashMap(void).init(allocator),1299 .names = std.StringHashMap(void).init(allocator),
1049 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Inst).init(allocator),1300 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
1301 .indent = 0,
1302 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
1050 };1303 };
1304 defer ctx.block_table.deinit();
1051 defer ctx.decls.deinit(allocator);1305 defer ctx.decls.deinit(allocator);
1052 defer ctx.names.deinit();1306 defer ctx.names.deinit();
1053 defer ctx.primitive_table.deinit();1307 defer ctx.primitive_table.deinit();
...@@ -1065,74 +1319,115 @@ const EmitZIR = struct {...@@ -1065,74 +1319,115 @@ const EmitZIR = struct {
1065 allocator: *Allocator,1319 allocator: *Allocator,
1066 arena: std.heap.ArenaAllocator,1320 arena: std.heap.ArenaAllocator,
1067 old_module: *const IrModule,1321 old_module: *const IrModule,
1068 decls: std.ArrayListUnmanaged(*Inst),1322 decls: std.ArrayListUnmanaged(*Decl),
1069 names: std.StringHashMap(void),1323 names: std.StringHashMap(void),
1070 next_auto_name: usize,1324 next_auto_name: usize,
1071 primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Inst),1325 primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Decl),
1326 indent: usize,
1327 block_table: std.AutoHashMap(*ir.Inst.Block, *Inst.Block),
10721328
1073 fn emit(self: *EmitZIR) !void {1329 fn emit(self: *EmitZIR) !void {
1074 // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced1330 // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced
1075 // by the hash table.1331 // by the hash table.
1076 var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator);1332 var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator);
1077 defer src_decls.deinit();1333 defer src_decls.deinit();
1078 try src_decls.ensureCapacity(self.old_module.decl_table.size);1334 try src_decls.ensureCapacity(self.old_module.decl_table.items().len);
1079 try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.size);1335 try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.items().len);
1080 try self.names.ensureCapacity(self.old_module.decl_table.size);1336 try self.names.ensureCapacity(self.old_module.decl_table.items().len);
10811337
1082 var decl_it = self.old_module.decl_table.iterator();1338 for (self.old_module.decl_table.items()) |entry| {
1083 while (decl_it.next()) |kv| {1339 const decl = entry.value;
1084 const decl = kv.value;
1085 src_decls.appendAssumeCapacity(decl);1340 src_decls.appendAssumeCapacity(decl);
1086 self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {});1341 self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {});
1087 }1342 }
1088 std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct {1343 std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct {
1089 fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool {1344 fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool {
1090 return a.src < b.src;1345 return a.src_index < b.src_index;
1091 }1346 }
1092 }).lessThan);1347 }).lessThan);
10931348
1094 // Emit all the decls.1349 // Emit all the decls.
1095 for (src_decls.items) |ir_decl| {1350 for (src_decls.items) |ir_decl| {
1096 if (self.old_module.export_owners.getValue(ir_decl)) |exports| {1351 switch (ir_decl.analysis) {
1352 .unreferenced => continue,
1353
1354 .complete => {},
1355 .codegen_failure => {}, // We still can emit the ZIR.
1356 .codegen_failure_retryable => {}, // We still can emit the ZIR.
1357
1358 .in_progress => unreachable,
1359 .outdated => unreachable,
1360
1361 .sema_failure,
1362 .sema_failure_retryable,
1363 .dependency_failure,
1364 => if (self.old_module.failed_decls.get(ir_decl)) |err_msg| {
1365 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
1366 fail_inst.* = .{
1367 .base = .{
1368 .src = ir_decl.src(),
1369 .tag = Inst.CompileError.base_tag,
1370 },
1371 .positionals = .{
1372 .msg = try self.arena.allocator.dupe(u8, err_msg.msg),
1373 },
1374 .kw_args = .{},
1375 };
1376 const decl = try self.arena.allocator.create(Decl);
1377 decl.* = .{
1378 .name = mem.spanZ(ir_decl.name),
1379 .contents_hash = undefined,
1380 .inst = &fail_inst.base,
1381 };
1382 try self.decls.append(self.allocator, decl);
1383 continue;
1384 },
1385 }
1386 if (self.old_module.export_owners.get(ir_decl)) |exports| {
1097 for (exports) |module_export| {1387 for (exports) |module_export| {
1098 const declval = try self.emitDeclVal(ir_decl.src, mem.spanZ(module_export.exported_decl.name));
1099 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);1388 const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
1100 const export_inst = try self.arena.allocator.create(Inst.Export);1389 const export_inst = try self.arena.allocator.create(Inst.Export);
1101 export_inst.* = .{1390 export_inst.* = .{
1102 .base = .{1391 .base = .{
1103 .name = try self.autoName(),
1104 .src = module_export.src,1392 .src = module_export.src,
1105 .tag = Inst.Export.base_tag,1393 .tag = Inst.Export.base_tag,
1106 },1394 },
1107 .positionals = .{1395 .positionals = .{
1108 .symbol_name = symbol_name,1396 .symbol_name = symbol_name.inst,
1109 .value = declval,1397 .decl_name = mem.spanZ(module_export.exported_decl.name),
1110 },1398 },
1111 .kw_args = .{},1399 .kw_args = .{},
1112 };1400 };
1113 try self.decls.append(self.allocator, &export_inst.base);1401 _ = try self.emitUnnamedDecl(&export_inst.base);
1114 }1402 }
1115 } else {1403 } else {
1116 const new_decl = try self.emitTypedValue(ir_decl.src, ir_decl.typed_value.most_recent.typed_value);1404 const new_decl = try self.emitTypedValue(ir_decl.src(), ir_decl.typed_value.most_recent.typed_value);
1117 new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name));1405 new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name));
1118 }1406 }
1119 }1407 }
1120 }1408 }
11211409
1122 fn resolveInst(self: *EmitZIR, inst_table: *std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {1410 const ZirBody = struct {
1411 inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
1412 instructions: *std.ArrayList(*Inst),
1413 };
1414
1415 fn resolveInst(self: *EmitZIR, new_body: ZirBody, inst: *ir.Inst) !*Inst {
1123 if (inst.cast(ir.Inst.Constant)) |const_inst| {1416 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1124 const new_decl = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {1417 const new_inst = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {
1125 const owner_decl = func_pl.func.owner_decl;1418 const owner_decl = func_pl.func.owner_decl;
1126 break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));1419 break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));
1127 } else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: {1420 } else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: {
1128 break :blk try self.emitDeclRef(inst.src, declref.decl);1421 const decl_ref = try self.emitDeclRef(inst.src, declref.decl);
1422 try new_body.instructions.append(decl_ref);
1423 break :blk decl_ref;
1129 } else blk: {1424 } else blk: {
1130 break :blk try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });1425 break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst;
1131 };1426 };
1132 try inst_table.putNoClobber(inst, new_decl);1427 try new_body.inst_table.putNoClobber(inst, new_inst);
1133 return new_decl;1428 return new_inst;
1134 } else {1429 } else {
1135 return inst_table.getValue(inst).?;1430 return new_body.inst_table.get(inst).?;
1136 }1431 }
1137 }1432 }
11381433
...@@ -1140,7 +1435,6 @@ const EmitZIR = struct {...@@ -1140,7 +1435,6 @@ const EmitZIR = struct {
1140 const declval = try self.arena.allocator.create(Inst.DeclVal);1435 const declval = try self.arena.allocator.create(Inst.DeclVal);
1141 declval.* = .{1436 declval.* = .{
1142 .base = .{1437 .base = .{
1143 .name = try self.autoName(),
1144 .src = src,1438 .src = src,
1145 .tag = Inst.DeclVal.base_tag,1439 .tag = Inst.DeclVal.base_tag,
1146 },1440 },
...@@ -1150,12 +1444,11 @@ const EmitZIR = struct {...@@ -1150,12 +1444,11 @@ const EmitZIR = struct {
1150 return &declval.base;1444 return &declval.base;
1151 }1445 }
11521446
1153 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst {1447 fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Decl {
1154 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);1448 const big_int_space = try self.arena.allocator.create(Value.BigIntSpace);
1155 const int_inst = try self.arena.allocator.create(Inst.Int);1449 const int_inst = try self.arena.allocator.create(Inst.Int);
1156 int_inst.* = .{1450 int_inst.* = .{
1157 .base = .{1451 .base = .{
1158 .name = try self.autoName(),
1159 .src = src,1452 .src = src,
1160 .tag = Inst.Int.base_tag,1453 .tag = Inst.Int.base_tag,
1161 },1454 },
...@@ -1164,34 +1457,29 @@ const EmitZIR = struct {...@@ -1164,34 +1457,29 @@ const EmitZIR = struct {
1164 },1457 },
1165 .kw_args = .{},1458 .kw_args = .{},
1166 };1459 };
1167 try self.decls.append(self.allocator, &int_inst.base);1460 return self.emitUnnamedDecl(&int_inst.base);
1168 return &int_inst.base;
1169 }1461 }
11701462
1171 fn emitDeclRef(self: *EmitZIR, src: usize, decl: *IrModule.Decl) !*Inst {1463 fn emitDeclRef(self: *EmitZIR, src: usize, module_decl: *IrModule.Decl) !*Inst {
1172 const declval = try self.emitDeclVal(src, mem.spanZ(decl.name));1464 const declref_inst = try self.arena.allocator.create(Inst.DeclRef);
1173 const ref_inst = try self.arena.allocator.create(Inst.Ref);1465 declref_inst.* = .{
1174 ref_inst.* = .{
1175 .base = .{1466 .base = .{
1176 .name = try self.autoName(),
1177 .src = src,1467 .src = src,
1178 .tag = Inst.Ref.base_tag,1468 .tag = Inst.DeclRef.base_tag,
1179 },1469 },
1180 .positionals = .{1470 .positionals = .{
1181 .operand = declval,1471 .name = mem.spanZ(module_decl.name),
1182 },1472 },
1183 .kw_args = .{},1473 .kw_args = .{},
1184 };1474 };
1185 try self.decls.append(self.allocator, &ref_inst.base);1475 return &declref_inst.base;
1186
1187 return &ref_inst.base;
1188 }1476 }
11891477
1190 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst {1478 fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Decl {
1191 const allocator = &self.arena.allocator;1479 const allocator = &self.arena.allocator;
1192 if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| {1480 if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| {
1193 const decl = decl_ref.decl;1481 const decl = decl_ref.decl;
1194 return self.emitDeclRef(src, decl);1482 return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));
1195 }1483 }
1196 switch (typed_value.ty.zigTypeTag()) {1484 switch (typed_value.ty.zigTypeTag()) {
1197 .Pointer => {1485 .Pointer => {
...@@ -1218,18 +1506,16 @@ const EmitZIR = struct {...@@ -1218,18 +1506,16 @@ const EmitZIR = struct {
1218 const as_inst = try self.arena.allocator.create(Inst.As);1506 const as_inst = try self.arena.allocator.create(Inst.As);
1219 as_inst.* = .{1507 as_inst.* = .{
1220 .base = .{1508 .base = .{
1221 .name = try self.autoName(),
1222 .src = src,1509 .src = src,
1223 .tag = Inst.As.base_tag,1510 .tag = Inst.As.base_tag,
1224 },1511 },
1225 .positionals = .{1512 .positionals = .{
1226 .dest_type = try self.emitType(src, typed_value.ty),1513 .dest_type = (try self.emitType(src, typed_value.ty)).inst,
1227 .value = try self.emitComptimeIntVal(src, typed_value.val),1514 .value = (try self.emitComptimeIntVal(src, typed_value.val)).inst,
1228 },1515 },
1229 .kw_args = .{},1516 .kw_args = .{},
1230 };1517 };
12311518 return self.emitUnnamedDecl(&as_inst.base);
1232 return &as_inst.base;
1233 },1519 },
1234 .Type => {1520 .Type => {
1235 const ty = typed_value.val.toType();1521 const ty = typed_value.val.toType();
...@@ -1251,11 +1537,10 @@ const EmitZIR = struct {...@@ -1251,11 +1537,10 @@ const EmitZIR = struct {
1251 try self.emitBody(body, &inst_table, &instructions);1537 try self.emitBody(body, &inst_table, &instructions);
1252 },1538 },
1253 .sema_failure => {1539 .sema_failure => {
1254 const err_msg = self.old_module.failed_decls.getValue(module_fn.owner_decl).?;1540 const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?;
1255 const fail_inst = try self.arena.allocator.create(Inst.CompileError);1541 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
1256 fail_inst.* = .{1542 fail_inst.* = .{
1257 .base = .{1543 .base = .{
1258 .name = try self.autoName(),
1259 .src = src,1544 .src = src,
1260 .tag = Inst.CompileError.base_tag,1545 .tag = Inst.CompileError.base_tag,
1261 },1546 },
...@@ -1270,7 +1555,6 @@ const EmitZIR = struct {...@@ -1270,7 +1555,6 @@ const EmitZIR = struct {
1270 const fail_inst = try self.arena.allocator.create(Inst.CompileError);1555 const fail_inst = try self.arena.allocator.create(Inst.CompileError);
1271 fail_inst.* = .{1556 fail_inst.* = .{
1272 .base = .{1557 .base = .{
1273 .name = try self.autoName(),
1274 .src = src,1558 .src = src,
1275 .tag = Inst.CompileError.base_tag,1559 .tag = Inst.CompileError.base_tag,
1276 },1560 },
...@@ -1283,7 +1567,7 @@ const EmitZIR = struct {...@@ -1283,7 +1567,7 @@ const EmitZIR = struct {
1283 },1567 },
1284 }1568 }
12851569
1286 const fn_type = try self.emitType(src, module_fn.fn_type);1570 const fn_type = try self.emitType(src, typed_value.ty);
12871571
1288 const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);1572 const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len);
1289 mem.copy(*Inst, arena_instrs, instructions.items);1573 mem.copy(*Inst, arena_instrs, instructions.items);
...@@ -1291,18 +1575,16 @@ const EmitZIR = struct {...@@ -1291,18 +1575,16 @@ const EmitZIR = struct {
1291 const fn_inst = try self.arena.allocator.create(Inst.Fn);1575 const fn_inst = try self.arena.allocator.create(Inst.Fn);
1292 fn_inst.* = .{1576 fn_inst.* = .{
1293 .base = .{1577 .base = .{
1294 .name = try self.autoName(),
1295 .src = src,1578 .src = src,
1296 .tag = Inst.Fn.base_tag,1579 .tag = Inst.Fn.base_tag,
1297 },1580 },
1298 .positionals = .{1581 .positionals = .{
1299 .fn_type = fn_type,1582 .fn_type = fn_type.inst,
1300 .body = .{ .instructions = arena_instrs },1583 .body = .{ .instructions = arena_instrs },
1301 },1584 },
1302 .kw_args = .{},1585 .kw_args = .{},
1303 };1586 };
1304 try self.decls.append(self.allocator, &fn_inst.base);1587 return self.emitUnnamedDecl(&fn_inst.base);
1305 return &fn_inst.base;
1306 },1588 },
1307 .Array => {1589 .Array => {
1308 // TODO more checks to make sure this can be emitted as a string literal1590 // TODO more checks to make sure this can be emitted as a string literal
...@@ -1318,7 +1600,6 @@ const EmitZIR = struct {...@@ -1318,7 +1600,6 @@ const EmitZIR = struct {
1318 const str_inst = try self.arena.allocator.create(Inst.Str);1600 const str_inst = try self.arena.allocator.create(Inst.Str);
1319 str_inst.* = .{1601 str_inst.* = .{
1320 .base = .{1602 .base = .{
1321 .name = try self.autoName(),
1322 .src = src,1603 .src = src,
1323 .tag = Inst.Str.base_tag,1604 .tag = Inst.Str.base_tag,
1324 },1605 },
...@@ -1327,8 +1608,7 @@ const EmitZIR = struct {...@@ -1327,8 +1608,7 @@ const EmitZIR = struct {
1327 },1608 },
1328 .kw_args = .{},1609 .kw_args = .{},
1329 };1610 };
1330 try self.decls.append(self.allocator, &str_inst.base);1611 return self.emitUnnamedDecl(&str_inst.base);
1331 return &str_inst.base;
1332 },1612 },
1333 .Void => return self.emitPrimitive(src, .void_value),1613 .Void => return self.emitPrimitive(src, .void_value),
1334 else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),1614 else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}),
...@@ -1339,7 +1619,6 @@ const EmitZIR = struct {...@@ -1339,7 +1619,6 @@ const EmitZIR = struct {
1339 const new_inst = try self.arena.allocator.create(T);1619 const new_inst = try self.arena.allocator.create(T);
1340 new_inst.* = .{1620 new_inst.* = .{
1341 .base = .{1621 .base = .{
1342 .name = try self.autoName(),
1343 .src = src,1622 .src = src,
1344 .tag = T.base_tag,1623 .tag = T.base_tag,
1345 },1624 },
...@@ -1351,29 +1630,150 @@ const EmitZIR = struct {...@@ -1351,29 +1630,150 @@ const EmitZIR = struct {
13511630
1352 fn emitBody(1631 fn emitBody(
1353 self: *EmitZIR,1632 self: *EmitZIR,
1354 body: IrModule.Body,1633 body: ir.Body,
1355 inst_table: *std.AutoHashMap(*ir.Inst, *Inst),1634 inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
1356 instructions: *std.ArrayList(*Inst),1635 instructions: *std.ArrayList(*Inst),
1357 ) Allocator.Error!void {1636 ) Allocator.Error!void {
1637 const new_body = ZirBody{
1638 .inst_table = inst_table,
1639 .instructions = instructions,
1640 };
1358 for (body.instructions) |inst| {1641 for (body.instructions) |inst| {
1359 const new_inst = switch (inst.tag) {1642 const new_inst = switch (inst.tag) {
1643 .not => blk: {
1644 const old_inst = inst.cast(ir.Inst.Not).?;
1645 assert(inst.ty.zigTypeTag() == .Bool);
1646 const new_inst = try self.arena.allocator.create(Inst.BoolNot);
1647 new_inst.* = .{
1648 .base = .{
1649 .src = inst.src,
1650 .tag = Inst.BoolNot.base_tag,
1651 },
1652 .positionals = .{
1653 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1654 },
1655 .kw_args = .{},
1656 };
1657 break :blk &new_inst.base;
1658 },
1659 .add => blk: {
1660 const old_inst = inst.cast(ir.Inst.Add).?;
1661 const new_inst = try self.arena.allocator.create(Inst.Add);
1662 new_inst.* = .{
1663 .base = .{
1664 .src = inst.src,
1665 .tag = Inst.Add.base_tag,
1666 },
1667 .positionals = .{
1668 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1669 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1670 },
1671 .kw_args = .{},
1672 };
1673 break :blk &new_inst.base;
1674 },
1675 .sub => blk: {
1676 const old_inst = inst.cast(ir.Inst.Sub).?;
1677 const new_inst = try self.arena.allocator.create(Inst.Sub);
1678 new_inst.* = .{
1679 .base = .{
1680 .src = inst.src,
1681 .tag = Inst.Sub.base_tag,
1682 },
1683 .positionals = .{
1684 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1685 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1686 },
1687 .kw_args = .{},
1688 };
1689 break :blk &new_inst.base;
1690 },
1691 .arg => blk: {
1692 const old_inst = inst.cast(ir.Inst.Arg).?;
1693 const new_inst = try self.arena.allocator.create(Inst.Arg);
1694 new_inst.* = .{
1695 .base = .{
1696 .src = inst.src,
1697 .tag = Inst.Arg.base_tag,
1698 },
1699 .positionals = .{},
1700 .kw_args = .{},
1701 };
1702 break :blk &new_inst.base;
1703 },
1704 .block => blk: {
1705 const old_inst = inst.cast(ir.Inst.Block).?;
1706 const new_inst = try self.arena.allocator.create(Inst.Block);
1707
1708 try self.block_table.put(old_inst, new_inst);
1709
1710 var block_body = std.ArrayList(*Inst).init(self.allocator);
1711 defer block_body.deinit();
1712
1713 try self.emitBody(old_inst.args.body, inst_table, &block_body);
1714
1715 new_inst.* = .{
1716 .base = .{
1717 .src = inst.src,
1718 .tag = Inst.Block.base_tag,
1719 },
1720 .positionals = .{
1721 .body = .{ .instructions = block_body.toOwnedSlice() },
1722 },
1723 .kw_args = .{},
1724 };
1725
1726 break :blk &new_inst.base;
1727 },
1728 .br => blk: {
1729 const old_inst = inst.cast(ir.Inst.Br).?;
1730 const new_block = self.block_table.get(old_inst.args.block).?;
1731 const new_inst = try self.arena.allocator.create(Inst.Break);
1732 new_inst.* = .{
1733 .base = .{
1734 .src = inst.src,
1735 .tag = Inst.Break.base_tag,
1736 },
1737 .positionals = .{
1738 .block = new_block,
1739 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1740 },
1741 .kw_args = .{},
1742 };
1743 break :blk &new_inst.base;
1744 },
1360 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),1745 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
1746 .brvoid => blk: {
1747 const old_inst = inst.cast(ir.Inst.BrVoid).?;
1748 const new_block = self.block_table.get(old_inst.args.block).?;
1749 const new_inst = try self.arena.allocator.create(Inst.BreakVoid);
1750 new_inst.* = .{
1751 .base = .{
1752 .src = inst.src,
1753 .tag = Inst.BreakVoid.base_tag,
1754 },
1755 .positionals = .{
1756 .block = new_block,
1757 },
1758 .kw_args = .{},
1759 };
1760 break :blk &new_inst.base;
1761 },
1361 .call => blk: {1762 .call => blk: {
1362 const old_inst = inst.cast(ir.Inst.Call).?;1763 const old_inst = inst.cast(ir.Inst.Call).?;
1363 const new_inst = try self.arena.allocator.create(Inst.Call);1764 const new_inst = try self.arena.allocator.create(Inst.Call);
13641765
1365 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);1766 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1366 for (args) |*elem, i| {1767 for (args) |*elem, i| {
1367 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);1768 elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
1368 }1769 }
1369 new_inst.* = .{1770 new_inst.* = .{
1370 .base = .{1771 .base = .{
1371 .name = try self.autoName(),
1372 .src = inst.src,1772 .src = inst.src,
1373 .tag = Inst.Call.base_tag,1773 .tag = Inst.Call.base_tag,
1374 },1774 },
1375 .positionals = .{1775 .positionals = .{
1376 .func = try self.resolveInst(inst_table, old_inst.args.func),1776 .func = try self.resolveInst(new_body, old_inst.args.func),
1377 .args = args,1777 .args = args,
1378 },1778 },
1379 .kw_args = .{},1779 .kw_args = .{},
...@@ -1381,7 +1781,22 @@ const EmitZIR = struct {...@@ -1381,7 +1781,22 @@ const EmitZIR = struct {
1381 break :blk &new_inst.base;1781 break :blk &new_inst.base;
1382 },1782 },
1383 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),1783 .unreach => try self.emitTrivial(inst.src, Inst.Unreachable),
1384 .ret => try self.emitTrivial(inst.src, Inst.Return),1784 .ret => blk: {
1785 const old_inst = inst.cast(ir.Inst.Ret).?;
1786 const new_inst = try self.arena.allocator.create(Inst.Return);
1787 new_inst.* = .{
1788 .base = .{
1789 .src = inst.src,
1790 .tag = Inst.Return.base_tag,
1791 },
1792 .positionals = .{
1793 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1794 },
1795 .kw_args = .{},
1796 };
1797 break :blk &new_inst.base;
1798 },
1799 .retvoid => try self.emitTrivial(inst.src, Inst.ReturnVoid),
1385 .constant => unreachable, // excluded from function bodies1800 .constant => unreachable, // excluded from function bodies
1386 .assembly => blk: {1801 .assembly => blk: {
1387 const old_inst = inst.cast(ir.Inst.Assembly).?;1802 const old_inst = inst.cast(ir.Inst.Assembly).?;
...@@ -1389,33 +1804,32 @@ const EmitZIR = struct {...@@ -1389,33 +1804,32 @@ const EmitZIR = struct {
13891804
1390 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);1805 const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len);
1391 for (inputs) |*elem, i| {1806 for (inputs) |*elem, i| {
1392 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]);1807 elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.inputs[i])).inst;
1393 }1808 }
13941809
1395 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);1810 const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len);
1396 for (clobbers) |*elem, i| {1811 for (clobbers) |*elem, i| {
1397 elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]);1812 elem.* = (try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i])).inst;
1398 }1813 }
13991814
1400 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);1815 const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
1401 for (args) |*elem, i| {1816 for (args) |*elem, i| {
1402 elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);1817 elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
1403 }1818 }
14041819
1405 new_inst.* = .{1820 new_inst.* = .{
1406 .base = .{1821 .base = .{
1407 .name = try self.autoName(),
1408 .src = inst.src,1822 .src = inst.src,
1409 .tag = Inst.Asm.base_tag,1823 .tag = Inst.Asm.base_tag,
1410 },1824 },
1411 .positionals = .{1825 .positionals = .{
1412 .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source),1826 .asm_source = (try self.emitStringLiteral(inst.src, old_inst.args.asm_source)).inst,
1413 .return_type = try self.emitType(inst.src, inst.ty),1827 .return_type = (try self.emitType(inst.src, inst.ty)).inst,
1414 },1828 },
1415 .kw_args = .{1829 .kw_args = .{
1416 .@"volatile" = old_inst.args.is_volatile,1830 .@"volatile" = old_inst.args.is_volatile,
1417 .output = if (old_inst.args.output) |o|1831 .output = if (old_inst.args.output) |o|
1418 try self.emitStringLiteral(inst.src, o)1832 (try self.emitStringLiteral(inst.src, o)).inst
1419 else1833 else
1420 null,1834 null,
1421 .inputs = inputs,1835 .inputs = inputs,
...@@ -1430,12 +1844,11 @@ const EmitZIR = struct {...@@ -1430,12 +1844,11 @@ const EmitZIR = struct {
1430 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);1844 const new_inst = try self.arena.allocator.create(Inst.PtrToInt);
1431 new_inst.* = .{1845 new_inst.* = .{
1432 .base = .{1846 .base = .{
1433 .name = try self.autoName(),
1434 .src = inst.src,1847 .src = inst.src,
1435 .tag = Inst.PtrToInt.base_tag,1848 .tag = Inst.PtrToInt.base_tag,
1436 },1849 },
1437 .positionals = .{1850 .positionals = .{
1438 .ptr = try self.resolveInst(inst_table, old_inst.args.ptr),1851 .ptr = try self.resolveInst(new_body, old_inst.args.ptr),
1439 },1852 },
1440 .kw_args = .{},1853 .kw_args = .{},
1441 };1854 };
...@@ -1446,13 +1859,12 @@ const EmitZIR = struct {...@@ -1446,13 +1859,12 @@ const EmitZIR = struct {
1446 const new_inst = try self.arena.allocator.create(Inst.BitCast);1859 const new_inst = try self.arena.allocator.create(Inst.BitCast);
1447 new_inst.* = .{1860 new_inst.* = .{
1448 .base = .{1861 .base = .{
1449 .name = try self.autoName(),
1450 .src = inst.src,1862 .src = inst.src,
1451 .tag = Inst.BitCast.base_tag,1863 .tag = Inst.BitCast.base_tag,
1452 },1864 },
1453 .positionals = .{1865 .positionals = .{
1454 .dest_type = try self.emitType(inst.src, inst.ty),1866 .dest_type = (try self.emitType(inst.src, inst.ty)).inst,
1455 .operand = try self.resolveInst(inst_table, old_inst.args.operand),1867 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1456 },1868 },
1457 .kw_args = .{},1869 .kw_args = .{},
1458 };1870 };
...@@ -1463,13 +1875,12 @@ const EmitZIR = struct {...@@ -1463,13 +1875,12 @@ const EmitZIR = struct {
1463 const new_inst = try self.arena.allocator.create(Inst.Cmp);1875 const new_inst = try self.arena.allocator.create(Inst.Cmp);
1464 new_inst.* = .{1876 new_inst.* = .{
1465 .base = .{1877 .base = .{
1466 .name = try self.autoName(),
1467 .src = inst.src,1878 .src = inst.src,
1468 .tag = Inst.Cmp.base_tag,1879 .tag = Inst.Cmp.base_tag,
1469 },1880 },
1470 .positionals = .{1881 .positionals = .{
1471 .lhs = try self.resolveInst(inst_table, old_inst.args.lhs),1882 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1472 .rhs = try self.resolveInst(inst_table, old_inst.args.rhs),1883 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1473 .op = old_inst.args.op,1884 .op = old_inst.args.op,
1474 },1885 },
1475 .kw_args = .{},1886 .kw_args = .{},
...@@ -1491,12 +1902,11 @@ const EmitZIR = struct {...@@ -1491,12 +1902,11 @@ const EmitZIR = struct {
1491 const new_inst = try self.arena.allocator.create(Inst.CondBr);1902 const new_inst = try self.arena.allocator.create(Inst.CondBr);
1492 new_inst.* = .{1903 new_inst.* = .{
1493 .base = .{1904 .base = .{
1494 .name = try self.autoName(),
1495 .src = inst.src,1905 .src = inst.src,
1496 .tag = Inst.CondBr.base_tag,1906 .tag = Inst.CondBr.base_tag,
1497 },1907 },
1498 .positionals = .{1908 .positionals = .{
1499 .condition = try self.resolveInst(inst_table, old_inst.args.condition),1909 .condition = try self.resolveInst(new_body, old_inst.args.condition),
1500 .true_body = .{ .instructions = true_body.toOwnedSlice() },1910 .true_body = .{ .instructions = true_body.toOwnedSlice() },
1501 .false_body = .{ .instructions = false_body.toOwnedSlice() },1911 .false_body = .{ .instructions = false_body.toOwnedSlice() },
1502 },1912 },
...@@ -1509,12 +1919,11 @@ const EmitZIR = struct {...@@ -1509,12 +1919,11 @@ const EmitZIR = struct {
1509 const new_inst = try self.arena.allocator.create(Inst.IsNull);1919 const new_inst = try self.arena.allocator.create(Inst.IsNull);
1510 new_inst.* = .{1920 new_inst.* = .{
1511 .base = .{1921 .base = .{
1512 .name = try self.autoName(),
1513 .src = inst.src,1922 .src = inst.src,
1514 .tag = Inst.IsNull.base_tag,1923 .tag = Inst.IsNull.base_tag,
1515 },1924 },
1516 .positionals = .{1925 .positionals = .{
1517 .operand = try self.resolveInst(inst_table, old_inst.args.operand),1926 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1518 },1927 },
1519 .kw_args = .{},1928 .kw_args = .{},
1520 };1929 };
...@@ -1525,12 +1934,11 @@ const EmitZIR = struct {...@@ -1525,12 +1934,11 @@ const EmitZIR = struct {
1525 const new_inst = try self.arena.allocator.create(Inst.IsNonNull);1934 const new_inst = try self.arena.allocator.create(Inst.IsNonNull);
1526 new_inst.* = .{1935 new_inst.* = .{
1527 .base = .{1936 .base = .{
1528 .name = try self.autoName(),
1529 .src = inst.src,1937 .src = inst.src,
1530 .tag = Inst.IsNonNull.base_tag,1938 .tag = Inst.IsNonNull.base_tag,
1531 },1939 },
1532 .positionals = .{1940 .positionals = .{
1533 .operand = try self.resolveInst(inst_table, old_inst.args.operand),1941 .operand = try self.resolveInst(new_body, old_inst.args.operand),
1534 },1942 },
1535 .kw_args = .{},1943 .kw_args = .{},
1536 };1944 };
...@@ -1538,12 +1946,20 @@ const EmitZIR = struct {...@@ -1538,12 +1946,20 @@ const EmitZIR = struct {
1538 },1946 },
1539 };1947 };
1540 try instructions.append(new_inst);1948 try instructions.append(new_inst);
1541 try inst_table.putNoClobber(inst, new_inst);1949 try inst_table.put(inst, new_inst);
1542 }1950 }
1543 }1951 }
15441952
1545 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst {1953 fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Decl {
1546 switch (ty.tag()) {1954 switch (ty.tag()) {
1955 .i8 => return self.emitPrimitive(src, .i8),
1956 .u8 => return self.emitPrimitive(src, .u8),
1957 .i16 => return self.emitPrimitive(src, .i16),
1958 .u16 => return self.emitPrimitive(src, .u16),
1959 .i32 => return self.emitPrimitive(src, .i32),
1960 .u32 => return self.emitPrimitive(src, .u32),
1961 .i64 => return self.emitPrimitive(src, .i64),
1962 .u64 => return self.emitPrimitive(src, .u64),
1547 .isize => return self.emitPrimitive(src, .isize),1963 .isize => return self.emitPrimitive(src, .isize),
1548 .usize => return self.emitPrimitive(src, .usize),1964 .usize => return self.emitPrimitive(src, .usize),
1549 .c_short => return self.emitPrimitive(src, .c_short),1965 .c_short => return self.emitPrimitive(src, .c_short),
...@@ -1575,26 +1991,44 @@ const EmitZIR = struct {...@@ -1575,26 +1991,44 @@ const EmitZIR = struct {
1575 ty.fnParamTypes(param_types);1991 ty.fnParamTypes(param_types);
1576 const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len);1992 const emitted_params = try self.arena.allocator.alloc(*Inst, param_types.len);
1577 for (param_types) |param_type, i| {1993 for (param_types) |param_type, i| {
1578 emitted_params[i] = try self.emitType(src, param_type);1994 emitted_params[i] = (try self.emitType(src, param_type)).inst;
1579 }1995 }
15801996
1581 const fntype_inst = try self.arena.allocator.create(Inst.FnType);1997 const fntype_inst = try self.arena.allocator.create(Inst.FnType);
1582 fntype_inst.* = .{1998 fntype_inst.* = .{
1583 .base = .{1999 .base = .{
1584 .name = try self.autoName(),
1585 .src = src,2000 .src = src,
1586 .tag = Inst.FnType.base_tag,2001 .tag = Inst.FnType.base_tag,
1587 },2002 },
1588 .positionals = .{2003 .positionals = .{
1589 .param_types = emitted_params,2004 .param_types = emitted_params,
1590 .return_type = try self.emitType(src, ty.fnReturnType()),2005 .return_type = (try self.emitType(src, ty.fnReturnType())).inst,
1591 },2006 },
1592 .kw_args = .{2007 .kw_args = .{
1593 .cc = ty.fnCallingConvention(),2008 .cc = ty.fnCallingConvention(),
1594 },2009 },
1595 };2010 };
1596 try self.decls.append(self.allocator, &fntype_inst.base);2011 return self.emitUnnamedDecl(&fntype_inst.base);
1597 return &fntype_inst.base;2012 },
2013 .Int => {
2014 const info = ty.intInfo(self.old_module.target());
2015 const signed = try self.emitPrimitive(src, if (info.signed) .@"true" else .@"false");
2016 const bits_payload = try self.arena.allocator.create(Value.Payload.Int_u64);
2017 bits_payload.* = .{ .int = info.bits };
2018 const bits = try self.emitComptimeIntVal(src, Value.initPayload(&bits_payload.base));
2019 const inttype_inst = try self.arena.allocator.create(Inst.IntType);
2020 inttype_inst.* = .{
2021 .base = .{
2022 .src = src,
2023 .tag = Inst.IntType.base_tag,
2024 },
2025 .positionals = .{
2026 .signed = signed.inst,
2027 .bits = bits.inst,
2028 },
2029 .kw_args = .{},
2030 };
2031 return self.emitUnnamedDecl(&inttype_inst.base);
1598 },2032 },
1599 else => std.debug.panic("TODO implement emitType for {}", .{ty}),2033 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
1600 },2034 },
...@@ -1607,19 +2041,18 @@ const EmitZIR = struct {...@@ -1607,19 +2041,18 @@ const EmitZIR = struct {
1607 self.next_auto_name += 1;2041 self.next_auto_name += 1;
1608 const gop = try self.names.getOrPut(proposed_name);2042 const gop = try self.names.getOrPut(proposed_name);
1609 if (!gop.found_existing) {2043 if (!gop.found_existing) {
1610 gop.kv.value = {};2044 gop.entry.value = {};
1611 return proposed_name;2045 return proposed_name;
1612 }2046 }
1613 }2047 }
1614 }2048 }
16152049
1616 fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Inst {2050 fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Decl {
1617 const gop = try self.primitive_table.getOrPut(tag);2051 const gop = try self.primitive_table.getOrPut(tag);
1618 if (!gop.found_existing) {2052 if (!gop.found_existing) {
1619 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);2053 const primitive_inst = try self.arena.allocator.create(Inst.Primitive);
1620 primitive_inst.* = .{2054 primitive_inst.* = .{
1621 .base = .{2055 .base = .{
1622 .name = try self.autoName(),
1623 .src = src,2056 .src = src,
1624 .tag = Inst.Primitive.base_tag,2057 .tag = Inst.Primitive.base_tag,
1625 },2058 },
...@@ -1628,17 +2061,15 @@ const EmitZIR = struct {...@@ -1628,17 +2061,15 @@ const EmitZIR = struct {
1628 },2061 },
1629 .kw_args = .{},2062 .kw_args = .{},
1630 };2063 };
1631 try self.decls.append(self.allocator, &primitive_inst.base);2064 gop.entry.value = try self.emitUnnamedDecl(&primitive_inst.base);
1632 gop.kv.value = &primitive_inst.base;
1633 }2065 }
1634 return gop.kv.value;2066 return gop.entry.value;
1635 }2067 }
16362068
1637 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst {2069 fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Decl {
1638 const str_inst = try self.arena.allocator.create(Inst.Str);2070 const str_inst = try self.arena.allocator.create(Inst.Str);
1639 str_inst.* = .{2071 str_inst.* = .{
1640 .base = .{2072 .base = .{
1641 .name = try self.autoName(),
1642 .src = src,2073 .src = src,
1643 .tag = Inst.Str.base_tag,2074 .tag = Inst.Str.base_tag,
1644 },2075 },
...@@ -1647,22 +2078,17 @@ const EmitZIR = struct {...@@ -1647,22 +2078,17 @@ const EmitZIR = struct {
1647 },2078 },
1648 .kw_args = .{},2079 .kw_args = .{},
1649 };2080 };
1650 try self.decls.append(self.allocator, &str_inst.base);2081 return self.emitUnnamedDecl(&str_inst.base);
2082 }
16512083
1652 const ref_inst = try self.arena.allocator.create(Inst.Ref);2084 fn emitUnnamedDecl(self: *EmitZIR, inst: *Inst) !*Decl {
1653 ref_inst.* = .{2085 const decl = try self.arena.allocator.create(Decl);
1654 .base = .{2086 decl.* = .{
1655 .name = try self.autoName(),2087 .name = try self.autoName(),
1656 .src = src,2088 .contents_hash = undefined,
1657 .tag = Inst.Ref.base_tag,2089 .inst = inst,
1658 },
1659 .positionals = .{
1660 .operand = &str_inst.base,
1661 },
1662 .kw_args = .{},
1663 };2090 };
1664 try self.decls.append(self.allocator, &ref_inst.base);2091 try self.decls.append(self.allocator, decl);
16652092 return decl;
1666 return &ref_inst.base;
1667 }2093 }
1668};2094};
src/all_types.hpp+25-4
...@@ -692,7 +692,7 @@ enum NodeType {...@@ -692,7 +692,7 @@ enum NodeType {
692 NodeTypeSuspend,692 NodeTypeSuspend,
693 NodeTypeAnyFrameType,693 NodeTypeAnyFrameType,
694 NodeTypeEnumLiteral,694 NodeTypeEnumLiteral,
695 NodeTypeVarFieldType,695 NodeTypeAnyTypeField,
696};696};
697697
698enum FnInline {698enum FnInline {
...@@ -705,7 +705,7 @@ struct AstNodeFnProto {...@@ -705,7 +705,7 @@ struct AstNodeFnProto {
705 Buf *name;705 Buf *name;
706 ZigList<AstNode *> params;706 ZigList<AstNode *> params;
707 AstNode *return_type;707 AstNode *return_type;
708 Token *return_var_token;708 Token *return_anytype_token;
709 AstNode *fn_def_node;709 AstNode *fn_def_node;
710 // populated if this is an extern declaration710 // populated if this is an extern declaration
711 Buf *lib_name;711 Buf *lib_name;
...@@ -734,7 +734,7 @@ struct AstNodeFnDef {...@@ -734,7 +734,7 @@ struct AstNodeFnDef {
734struct AstNodeParamDecl {734struct AstNodeParamDecl {
735 Buf *name;735 Buf *name;
736 AstNode *type;736 AstNode *type;
737 Token *var_token;737 Token *anytype_token;
738 Buf doc_comments;738 Buf doc_comments;
739 bool is_noalias;739 bool is_noalias;
740 bool is_comptime;740 bool is_comptime;
...@@ -1827,6 +1827,7 @@ enum BuiltinFnId {...@@ -1827,6 +1827,7 @@ enum BuiltinFnId {
1827 BuiltinFnIdBitSizeof,1827 BuiltinFnIdBitSizeof,
1828 BuiltinFnIdWasmMemorySize,1828 BuiltinFnIdWasmMemorySize,
1829 BuiltinFnIdWasmMemoryGrow,1829 BuiltinFnIdWasmMemoryGrow,
1830 BuiltinFnIdSrc,
1830};1831};
18311832
1832struct BuiltinFnEntry {1833struct BuiltinFnEntry {
...@@ -2144,7 +2145,7 @@ struct CodeGen {...@@ -2144,7 +2145,7 @@ struct CodeGen {
2144 ZigType *entry_num_lit_float;2145 ZigType *entry_num_lit_float;
2145 ZigType *entry_undef;2146 ZigType *entry_undef;
2146 ZigType *entry_null;2147 ZigType *entry_null;
2147 ZigType *entry_var;2148 ZigType *entry_anytype;
2148 ZigType *entry_global_error_set;2149 ZigType *entry_global_error_set;
2149 ZigType *entry_enum_literal;2150 ZigType *entry_enum_literal;
2150 ZigType *entry_any_frame;2151 ZigType *entry_any_frame;
...@@ -2640,6 +2641,7 @@ enum IrInstSrcId {...@@ -2640,6 +2641,7 @@ enum IrInstSrcId {
2640 IrInstSrcIdCall,2641 IrInstSrcIdCall,
2641 IrInstSrcIdCallArgs,2642 IrInstSrcIdCallArgs,
2642 IrInstSrcIdCallExtra,2643 IrInstSrcIdCallExtra,
2644 IrInstSrcIdAsyncCallExtra,
2643 IrInstSrcIdConst,2645 IrInstSrcIdConst,
2644 IrInstSrcIdReturn,2646 IrInstSrcIdReturn,
2645 IrInstSrcIdContainerInitList,2647 IrInstSrcIdContainerInitList,
...@@ -2754,6 +2756,7 @@ enum IrInstSrcId {...@@ -2754,6 +2756,7 @@ enum IrInstSrcId {
2754 IrInstSrcIdSpillEnd,2756 IrInstSrcIdSpillEnd,
2755 IrInstSrcIdWasmMemorySize,2757 IrInstSrcIdWasmMemorySize,
2756 IrInstSrcIdWasmMemoryGrow,2758 IrInstSrcIdWasmMemoryGrow,
2759 IrInstSrcIdSrc,
2757};2760};
27582761
2759// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR.2762// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR.
...@@ -3253,6 +3256,20 @@ struct IrInstSrcCallExtra {...@@ -3253,6 +3256,20 @@ struct IrInstSrcCallExtra {
3253 ResultLoc *result_loc;3256 ResultLoc *result_loc;
3254};3257};
32553258
3259// This is a pass1 instruction, used by @asyncCall, when the args node
3260// is not a literal.
3261// `args` is expected to be either a struct or a tuple.
3262struct IrInstSrcAsyncCallExtra {
3263 IrInstSrc base;
3264
3265 CallModifier modifier;
3266 IrInstSrc *fn_ref;
3267 IrInstSrc *ret_ptr;
3268 IrInstSrc *new_stack;
3269 IrInstSrc *args;
3270 ResultLoc *result_loc;
3271};
3272
3256struct IrInstGenCall {3273struct IrInstGenCall {
3257 IrInstGen base;3274 IrInstGen base;
32583275
...@@ -3761,6 +3778,10 @@ struct IrInstGenWasmMemoryGrow {...@@ -3761,6 +3778,10 @@ struct IrInstGenWasmMemoryGrow {
3761 IrInstGen *delta;3778 IrInstGen *delta;
3762};3779};
37633780
3781struct IrInstSrcSrc {
3782 IrInstSrc base;
3783};
3784
3764struct IrInstSrcSlice {3785struct IrInstSrcSlice {
3765 IrInstSrc base;3786 IrInstSrc base;
37663787
src/analyze.cpp+40-14
...@@ -1129,7 +1129,7 @@ ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *...@@ -1129,7 +1129,7 @@ ZigValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *
1129 ZigValue *result = g->pass1_arena->create<ZigValue>();1129 ZigValue *result = g->pass1_arena->create<ZigValue>();
1130 ZigValue *result_ptr = g->pass1_arena->create<ZigValue>();1130 ZigValue *result_ptr = g->pass1_arena->create<ZigValue>();
1131 result->special = ConstValSpecialUndef;1131 result->special = ConstValSpecialUndef;
1132 result->type = (type_entry == nullptr) ? g->builtin_types.entry_var : type_entry;1132 result->type = (type_entry == nullptr) ? g->builtin_types.entry_anytype : type_entry;
1133 result_ptr->special = ConstValSpecialStatic;1133 result_ptr->special = ConstValSpecialStatic;
1134 result_ptr->type = get_pointer_to_type(g, result->type, false);1134 result_ptr->type = get_pointer_to_type(g, result->type, false);
1135 result_ptr->data.x_ptr.mut = ConstPtrMutComptimeVar;1135 result_ptr->data.x_ptr.mut = ConstPtrMutComptimeVar;
...@@ -1230,7 +1230,7 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent...@@ -1230,7 +1230,7 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent
1230Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_opaque_type) {1230Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_opaque_type) {
1231 if (type_val->special != ConstValSpecialLazy) {1231 if (type_val->special != ConstValSpecialLazy) {
1232 assert(type_val->special == ConstValSpecialStatic);1232 assert(type_val->special == ConstValSpecialStatic);
1233 if (type_val->data.x_type == g->builtin_types.entry_var) {1233 if (type_val->data.x_type == g->builtin_types.entry_anytype) {
1234 *is_opaque_type = false;1234 *is_opaque_type = false;
1235 return ErrorNone;1235 return ErrorNone;
1236 }1236 }
...@@ -1511,13 +1511,13 @@ ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {...@@ -1511,13 +1511,13 @@ ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
1511 }1511 }
1512 for (; i < fn_type_id->param_count; i += 1) {1512 for (; i < fn_type_id->param_count; i += 1) {
1513 const char *comma_str = (i == 0) ? "" : ",";1513 const char *comma_str = (i == 0) ? "" : ",";
1514 buf_appendf(&fn_type->name, "%svar", comma_str);1514 buf_appendf(&fn_type->name, "%sanytype", comma_str);
1515 }1515 }
1516 buf_append_str(&fn_type->name, ")");1516 buf_append_str(&fn_type->name, ")");
1517 if (fn_type_id->cc != CallingConventionUnspecified) {1517 if (fn_type_id->cc != CallingConventionUnspecified) {
1518 buf_appendf(&fn_type->name, " callconv(.%s)", calling_convention_name(fn_type_id->cc));1518 buf_appendf(&fn_type->name, " callconv(.%s)", calling_convention_name(fn_type_id->cc));
1519 }1519 }
1520 buf_append_str(&fn_type->name, " var");1520 buf_append_str(&fn_type->name, " anytype");
15211521
1522 fn_type->data.fn.fn_type_id = *fn_type_id;1522 fn_type->data.fn.fn_type_id = *fn_type_id;
1523 fn_type->data.fn.is_generic = true;1523 fn_type->data.fn.is_generic = true;
...@@ -1853,10 +1853,10 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc...@@ -1853,10 +1853,10 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
1853 buf_sprintf("var args only allowed in functions with C calling convention"));1853 buf_sprintf("var args only allowed in functions with C calling convention"));
1854 return g->builtin_types.entry_invalid;1854 return g->builtin_types.entry_invalid;
1855 }1855 }
1856 } else if (param_node->data.param_decl.var_token != nullptr) {1856 } else if (param_node->data.param_decl.anytype_token != nullptr) {
1857 if (!calling_convention_allows_zig_types(fn_type_id.cc)) {1857 if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
1858 add_node_error(g, param_node,1858 add_node_error(g, param_node,
1859 buf_sprintf("parameter of type 'var' not allowed in function with calling convention '%s'",1859 buf_sprintf("parameter of type 'anytype' not allowed in function with calling convention '%s'",
1860 calling_convention_name(fn_type_id.cc)));1860 calling_convention_name(fn_type_id.cc)));
1861 return g->builtin_types.entry_invalid;1861 return g->builtin_types.entry_invalid;
1862 }1862 }
...@@ -1942,10 +1942,10 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc...@@ -1942,10 +1942,10 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
1942 fn_entry->align_bytes = fn_type_id.alignment;1942 fn_entry->align_bytes = fn_type_id.alignment;
1943 }1943 }
19441944
1945 if (fn_proto->return_var_token != nullptr) {1945 if (fn_proto->return_anytype_token != nullptr) {
1946 if (!calling_convention_allows_zig_types(fn_type_id.cc)) {1946 if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
1947 add_node_error(g, fn_proto->return_type,1947 add_node_error(g, fn_proto->return_type,
1948 buf_sprintf("return type 'var' not allowed in function with calling convention '%s'",1948 buf_sprintf("return type 'anytype' not allowed in function with calling convention '%s'",
1949 calling_convention_name(fn_type_id.cc)));1949 calling_convention_name(fn_type_id.cc)));
1950 return g->builtin_types.entry_invalid;1950 return g->builtin_types.entry_invalid;
1951 }1951 }
...@@ -3802,7 +3802,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {...@@ -3802,7 +3802,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
3802 case NodeTypeEnumLiteral:3802 case NodeTypeEnumLiteral:
3803 case NodeTypeAnyFrameType:3803 case NodeTypeAnyFrameType:
3804 case NodeTypeErrorSetField:3804 case NodeTypeErrorSetField:
3805 case NodeTypeVarFieldType:3805 case NodeTypeAnyTypeField:
3806 zig_unreachable();3806 zig_unreachable();
3807 }3807 }
3808}3808}
...@@ -3823,15 +3823,18 @@ static Error resolve_decl_container(CodeGen *g, TldContainer *tld_container) {...@@ -3823,15 +3823,18 @@ static Error resolve_decl_container(CodeGen *g, TldContainer *tld_container) {
3823 }3823 }
3824}3824}
38253825
3826ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry) {3826ZigType *validate_var_type(CodeGen *g, AstNodeVariableDeclaration *source_node, ZigType *type_entry) {
3827 switch (type_entry->id) {3827 switch (type_entry->id) {
3828 case ZigTypeIdInvalid:3828 case ZigTypeIdInvalid:
3829 return g->builtin_types.entry_invalid;3829 return g->builtin_types.entry_invalid;
3830 case ZigTypeIdOpaque:
3831 if (source_node->is_extern)
3832 return type_entry;
3833 ZIG_FALLTHROUGH;
3830 case ZigTypeIdUnreachable:3834 case ZigTypeIdUnreachable:
3831 case ZigTypeIdUndefined:3835 case ZigTypeIdUndefined:
3832 case ZigTypeIdNull:3836 case ZigTypeIdNull:
3833 case ZigTypeIdOpaque:3837 add_node_error(g, source_node->type, buf_sprintf("variable of type '%s' not allowed",
3834 add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed",
3835 buf_ptr(&type_entry->name)));3838 buf_ptr(&type_entry->name)));
3836 return g->builtin_types.entry_invalid;3839 return g->builtin_types.entry_invalid;
3837 case ZigTypeIdComptimeFloat:3840 case ZigTypeIdComptimeFloat:
...@@ -3973,7 +3976,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {...@@ -3973,7 +3976,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
3973 } else {3976 } else {
3974 tld_var->analyzing_type = true;3977 tld_var->analyzing_type = true;
3975 ZigType *proposed_type = analyze_type_expr(g, tld_var->base.parent_scope, var_decl->type);3978 ZigType *proposed_type = analyze_type_expr(g, tld_var->base.parent_scope, var_decl->type);
3976 explicit_type = validate_var_type(g, var_decl->type, proposed_type);3979 explicit_type = validate_var_type(g, var_decl, proposed_type);
3977 }3980 }
3978 }3981 }
39793982
...@@ -4012,6 +4015,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {...@@ -4012,6 +4015,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
4012 } else if (!is_extern) {4015 } else if (!is_extern) {
4013 add_node_error(g, source_node, buf_sprintf("variables must be initialized"));4016 add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
4014 implicit_type = g->builtin_types.entry_invalid;4017 implicit_type = g->builtin_types.entry_invalid;
4018 } else if (explicit_type == nullptr) {
4019 // extern variable without explicit type
4020 add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
4021 implicit_type = g->builtin_types.entry_invalid;
4015 }4022 }
40164023
4017 ZigType *type = explicit_type ? explicit_type : implicit_type;4024 ZigType *type = explicit_type ? explicit_type : implicit_type;
...@@ -5864,7 +5871,7 @@ ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {...@@ -5864,7 +5871,7 @@ ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {
58645871
5865ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) {5872ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) {
5866 Error err;5873 Error err;
5867 if (ty == g->builtin_types.entry_var) {5874 if (ty == g->builtin_types.entry_anytype) {
5868 return ReqCompTimeYes;5875 return ReqCompTimeYes;
5869 }5876 }
5870 switch (ty->id) {5877 switch (ty->id) {
...@@ -6012,6 +6019,19 @@ ZigValue *create_const_null(CodeGen *g, ZigType *type) {...@@ -6012,6 +6019,19 @@ ZigValue *create_const_null(CodeGen *g, ZigType *type) {
6012 return const_val;6019 return const_val;
6013}6020}
60146021
6022void init_const_fn(ZigValue *const_val, ZigFn *fn) {
6023 const_val->special = ConstValSpecialStatic;
6024 const_val->type = fn->type_entry;
6025 const_val->data.x_ptr.special = ConstPtrSpecialFunction;
6026 const_val->data.x_ptr.data.fn.fn_entry = fn;
6027}
6028
6029ZigValue *create_const_fn(CodeGen *g, ZigFn *fn) {
6030 ZigValue *const_val = g->pass1_arena->create<ZigValue>();
6031 init_const_fn(const_val, fn);
6032 return const_val;
6033}
6034
6015void init_const_float(ZigValue *const_val, ZigType *type, double value) {6035void init_const_float(ZigValue *const_val, ZigType *type, double value) {
6016 const_val->special = ConstValSpecialStatic;6036 const_val->special = ConstValSpecialStatic;
6017 const_val->type = type;6037 const_val->type = type;
...@@ -9584,6 +9604,12 @@ void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src) {...@@ -9584,6 +9604,12 @@ void copy_const_val(CodeGen *g, ZigValue *dest, ZigValue *src) {
9584 break;9604 break;
9585 }9605 }
9586 }9606 }
9607 } else if (dest->type->id == ZigTypeIdUnion) {
9608 bigint_init_bigint(&dest->data.x_union.tag, &src->data.x_union.tag);
9609 dest->data.x_union.payload = g->pass1_arena->create<ZigValue>();
9610 copy_const_val(g, dest->data.x_union.payload, src->data.x_union.payload);
9611 dest->data.x_union.payload->parent.id = ConstParentIdUnion;
9612 dest->data.x_union.payload->parent.data.p_union.union_val = dest;
9587 } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) {9613 } else if (type_has_optional_repr(dest->type) && dest->data.x_optional != nullptr) {
9588 dest->data.x_optional = g->pass1_arena->create<ZigValue>();9614 dest->data.x_optional = g->pass1_arena->create<ZigValue>();
9589 copy_const_val(g, dest->data.x_optional, src->data.x_optional);9615 copy_const_val(g, dest->data.x_optional, src->data.x_optional);
src/analyze.hpp+4-1
...@@ -77,7 +77,7 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool all...@@ -77,7 +77,7 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool all
77ZigType *get_src_ptr_type(ZigType *type);77ZigType *get_src_ptr_type(ZigType *type);
78uint32_t get_ptr_align(CodeGen *g, ZigType *type);78uint32_t get_ptr_align(CodeGen *g, ZigType *type);
79bool get_ptr_const(CodeGen *g, ZigType *type);79bool get_ptr_const(CodeGen *g, ZigType *type);
80ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry);80ZigType *validate_var_type(CodeGen *g, AstNodeVariableDeclaration *source_node, ZigType *type_entry);
81ZigType *container_ref_type(ZigType *type_entry);81ZigType *container_ref_type(ZigType *type_entry);
82bool type_is_complete(ZigType *type_entry);82bool type_is_complete(ZigType *type_entry);
83bool type_is_resolved(ZigType *type_entry, ResolveStatus status);83bool type_is_resolved(ZigType *type_entry, ResolveStatus status);
...@@ -180,6 +180,9 @@ ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size...@@ -180,6 +180,9 @@ ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size
180void init_const_null(ZigValue *const_val, ZigType *type);180void init_const_null(ZigValue *const_val, ZigType *type);
181ZigValue *create_const_null(CodeGen *g, ZigType *type);181ZigValue *create_const_null(CodeGen *g, ZigType *type);
182182
183void init_const_fn(ZigValue *const_val, ZigFn *fn);
184ZigValue *create_const_fn(CodeGen *g, ZigFn *fn);
185
183ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count);186ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count);
184ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count);187ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count);
185188
src/ast_render.cpp+8-8
...@@ -270,8 +270,8 @@ static const char *node_type_str(NodeType node_type) {...@@ -270,8 +270,8 @@ static const char *node_type_str(NodeType node_type) {
270 return "EnumLiteral";270 return "EnumLiteral";
271 case NodeTypeErrorSetField:271 case NodeTypeErrorSetField:
272 return "ErrorSetField";272 return "ErrorSetField";
273 case NodeTypeVarFieldType:273 case NodeTypeAnyTypeField:
274 return "VarFieldType";274 return "AnyTypeField";
275 }275 }
276 zig_unreachable();276 zig_unreachable();
277}277}
...@@ -466,8 +466,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -466,8 +466,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
466 }466 }
467 if (param_decl->data.param_decl.is_var_args) {467 if (param_decl->data.param_decl.is_var_args) {
468 fprintf(ar->f, "...");468 fprintf(ar->f, "...");
469 } else if (param_decl->data.param_decl.var_token != nullptr) {469 } else if (param_decl->data.param_decl.anytype_token != nullptr) {
470 fprintf(ar->f, "var");470 fprintf(ar->f, "anytype");
471 } else {471 } else {
472 render_node_grouped(ar, param_decl->data.param_decl.type);472 render_node_grouped(ar, param_decl->data.param_decl.type);
473 }473 }
...@@ -496,8 +496,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -496,8 +496,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
496 fprintf(ar->f, ")");496 fprintf(ar->f, ")");
497 }497 }
498498
499 if (node->data.fn_proto.return_var_token != nullptr) {499 if (node->data.fn_proto.return_anytype_token != nullptr) {
500 fprintf(ar->f, "var");500 fprintf(ar->f, "anytype");
501 } else {501 } else {
502 AstNode *return_type_node = node->data.fn_proto.return_type;502 AstNode *return_type_node = node->data.fn_proto.return_type;
503 assert(return_type_node != nullptr);503 assert(return_type_node != nullptr);
...@@ -1216,8 +1216,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -1216,8 +1216,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
1216 fprintf(ar->f, ".%s", buf_ptr(&node->data.enum_literal.identifier->data.str_lit.str));1216 fprintf(ar->f, ".%s", buf_ptr(&node->data.enum_literal.identifier->data.str_lit.str));
1217 break;1217 break;
1218 }1218 }
1219 case NodeTypeVarFieldType: {1219 case NodeTypeAnyTypeField: {
1220 fprintf(ar->f, "var");1220 fprintf(ar->f, "anytype");
1221 break;1221 break;
1222 }1222 }
1223 case NodeTypeParamDecl:1223 case NodeTypeParamDecl:
src/codegen.cpp+33-14
...@@ -1535,9 +1535,11 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z...@@ -1535,9 +1535,11 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
1535 zig_unreachable();1535 zig_unreachable();
1536 }1536 }
15371537
1538 if (actual_type->id == ZigTypeIdInt &&1538 if (actual_type->id == ZigTypeIdInt && want_runtime_safety && (
1539 !wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed &&1539 // negative to unsigned
1540 want_runtime_safety)1540 (!wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed) ||
1541 // unsigned would become negative
1542 (wanted_type->data.integral.is_signed && !actual_type->data.integral.is_signed && actual_bits == wanted_bits)))
1541 {1543 {
1542 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, actual_type));1544 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, actual_type));
1543 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, "");1545 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, "");
...@@ -1547,7 +1549,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z...@@ -1547,7 +1549,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
1547 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);1549 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
15481550
1549 LLVMPositionBuilderAtEnd(g->builder, fail_block);1551 LLVMPositionBuilderAtEnd(g->builder, fail_block);
1550 gen_safety_crash(g, PanicMsgIdCastNegativeToUnsigned);1552 gen_safety_crash(g, actual_type->data.integral.is_signed ? PanicMsgIdCastNegativeToUnsigned : PanicMsgIdCastTruncatedData);
15511553
1552 LLVMPositionBuilderAtEnd(g->builder, ok_block);1554 LLVMPositionBuilderAtEnd(g->builder, ok_block);
1553 }1555 }
...@@ -3540,7 +3542,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl...@@ -3540,7 +3542,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl
35403542
3541 for (size_t field_i = 0; field_i < field_count; field_i += 1) {3543 for (size_t field_i = 0; field_i < field_count; field_i += 1) {
3542 TypeEnumField *type_enum_field = &wanted_type->data.enumeration.fields[field_i];3544 TypeEnumField *type_enum_field = &wanted_type->data.enumeration.fields[field_i];
3543 3545
3544 Buf *name = type_enum_field->name;3546 Buf *name = type_enum_field->name;
3545 auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);3547 auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);
3546 if (entry != nullptr) {3548 if (entry != nullptr) {
...@@ -3654,7 +3656,7 @@ static LLVMValueRef ir_gen_negation(CodeGen *g, IrInstGen *inst, IrInstGen *oper...@@ -3654,7 +3656,7 @@ static LLVMValueRef ir_gen_negation(CodeGen *g, IrInstGen *inst, IrInstGen *oper
3654 } else if (scalar_type->data.integral.is_signed) {3656 } else if (scalar_type->data.integral.is_signed) {
3655 return LLVMBuildNSWNeg(g->builder, llvm_operand, "");3657 return LLVMBuildNSWNeg(g->builder, llvm_operand, "");
3656 } else {3658 } else {
3657 return LLVMBuildNUWNeg(g->builder, llvm_operand, "");3659 zig_unreachable();
3658 }3660 }
3659 } else {3661 } else {
3660 zig_unreachable();3662 zig_unreachable();
...@@ -3984,7 +3986,7 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutableGen *executable,...@@ -3984,7 +3986,7 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutableGen *executable,
3984 assert(array_type->data.pointer.child_type->id == ZigTypeIdArray);3986 assert(array_type->data.pointer.child_type->id == ZigTypeIdArray);
3985 array_type = array_type->data.pointer.child_type;3987 array_type = array_type->data.pointer.child_type;
3986 }3988 }
3987 3989
3988 assert(array_type->data.array.len != 0 || array_type->data.array.sentinel != nullptr);3990 assert(array_type->data.array.len != 0 || array_type->data.array.sentinel != nullptr);
39893991
3990 if (safety_check_on) {3992 if (safety_check_on) {
...@@ -5258,7 +5260,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {...@@ -5258,7 +5260,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {
52585260
5259 for (size_t field_i = 0; field_i < field_count; field_i += 1) {5261 for (size_t field_i = 0; field_i < field_count; field_i += 1) {
5260 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];5262 TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
5261 5263
5262 Buf *name = type_enum_field->name;5264 Buf *name = type_enum_field->name;
5263 auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);5265 auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);
5264 if (entry != nullptr) {5266 if (entry != nullptr) {
...@@ -5471,7 +5473,7 @@ static LLVMTypeRef get_atomic_abi_type(CodeGen *g, IrInstGen *instruction) {...@@ -5471,7 +5473,7 @@ static LLVMTypeRef get_atomic_abi_type(CodeGen *g, IrInstGen *instruction) {
5471 }5473 }
5472 auto bit_count = operand_type->data.integral.bit_count;5474 auto bit_count = operand_type->data.integral.bit_count;
5473 bool is_signed = operand_type->data.integral.is_signed;5475 bool is_signed = operand_type->data.integral.is_signed;
5474 5476
5475 ir_assert(bit_count != 0, instruction);5477 ir_assert(bit_count != 0, instruction);
5476 if (bit_count == 1 || !is_power_of_2(bit_count)) {5478 if (bit_count == 1 || !is_power_of_2(bit_count)) {
5477 return get_llvm_type(g, get_int_type(g, is_signed, operand_type->abi_size * 8));5479 return get_llvm_type(g, get_int_type(g, is_signed, operand_type->abi_size * 8));
...@@ -5583,8 +5585,12 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, Ir...@@ -5583,8 +5585,12 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, Ir
55835585
5584 bool val_is_undef = value_is_all_undef(g, instruction->byte->value);5586 bool val_is_undef = value_is_all_undef(g, instruction->byte->value);
5585 LLVMValueRef fill_char;5587 LLVMValueRef fill_char;
5586 if (val_is_undef && ir_want_runtime_safety_scope(g, instruction->base.base.scope)) {5588 if (val_is_undef) {
5587 fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);5589 if (ir_want_runtime_safety_scope(g, instruction->base.base.scope)) {
5590 fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
5591 } else {
5592 return nullptr;
5593 }
5588 } else {5594 } else {
5589 fill_char = ir_llvm_value(g, instruction->byte);5595 fill_char = ir_llvm_value(g, instruction->byte);
5590 }5596 }
...@@ -7473,6 +7479,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n...@@ -7473,6 +7479,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
7473 continue;7479 continue;
7474 }7480 }
7475 ZigValue *field_val = const_val->data.x_struct.fields[i];7481 ZigValue *field_val = const_val->data.x_struct.fields[i];
7482 if (field_val == nullptr) {
7483 add_node_error(g, type_struct_field->decl_node,
7484 buf_sprintf("compiler bug: generating const value for struct field '%s'",
7485 buf_ptr(type_struct_field->name)));
7486 codegen_report_errors_and_exit(g);
7487 }
7476 ZigType *field_type = field_val->type;7488 ZigType *field_type = field_val->type;
7477 assert(field_type != nullptr);7489 assert(field_type != nullptr);
7478 if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) {7490 if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) {
...@@ -8436,8 +8448,8 @@ static void define_builtin_types(CodeGen *g) {...@@ -8436,8 +8448,8 @@ static void define_builtin_types(CodeGen *g) {
8436 }8448 }
8437 {8449 {
8438 ZigType *entry = new_type_table_entry(ZigTypeIdOpaque);8450 ZigType *entry = new_type_table_entry(ZigTypeIdOpaque);
8439 buf_init_from_str(&entry->name, "(var)");8451 buf_init_from_str(&entry->name, "(anytype)");
8440 g->builtin_types.entry_var = entry;8452 g->builtin_types.entry_anytype = entry;
8441 }8453 }
84428454
8443 for (size_t i = 0; i < array_length(c_int_type_infos); i += 1) {8455 for (size_t i = 0; i < array_length(c_int_type_infos); i += 1) {
...@@ -8714,6 +8726,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -8714,6 +8726,7 @@ static void define_builtin_fns(CodeGen *g) {
8714 create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1);8726 create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1);
8715 create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1);8727 create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1);
8716 create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);8728 create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);
8729 create_builtin_fn(g, BuiltinFnIdSrc, "src", 0);
8717}8730}
87188731
8719static const char *bool_to_str(bool b) {8732static const char *bool_to_str(bool b) {
...@@ -9264,7 +9277,7 @@ static void init(CodeGen *g) {...@@ -9264,7 +9277,7 @@ static void init(CodeGen *g) {
9264 abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32" : "lp64";9277 abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32" : "lp64";
9265 }9278 }
9266 }9279 }
9267 9280
9268 g->target_machine = ZigLLVMCreateTargetMachine(target_ref, buf_ptr(&g->llvm_triple_str),9281 g->target_machine = ZigLLVMCreateTargetMachine(target_ref, buf_ptr(&g->llvm_triple_str),
9269 target_specific_cpu_args, target_specific_features, opt_level, reloc_mode,9282 target_specific_cpu_args, target_specific_features, opt_level, reloc_mode,
9270 to_llvm_code_model(g), g->function_sections, float_abi, abi_name);9283 to_llvm_code_model(g), g->function_sections, float_abi, abi_name);
...@@ -9464,9 +9477,15 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa...@@ -9464,9 +9477,15 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa
9464 const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include",9477 const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include",
9465 buf_ptr(g->zig_lib_dir)));9478 buf_ptr(g->zig_lib_dir)));
94669479
9480 const char *libcxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include",
9481 buf_ptr(g->zig_lib_dir)));
9482
9467 args.append("-isystem");9483 args.append("-isystem");
9468 args.append(libcxx_include_path);9484 args.append(libcxx_include_path);
94699485
9486 args.append("-isystem");
9487 args.append(libcxxabi_include_path);
9488
9470 if (target_abi_is_musl(g->zig_target->abi)) {9489 if (target_abi_is_musl(g->zig_target->abi)) {
9471 args.append("-D_LIBCPP_HAS_MUSL_LIBC");9490 args.append("-D_LIBCPP_HAS_MUSL_LIBC");
9472 }9491 }
src/compiler.cpp+8
...@@ -38,11 +38,19 @@ Error get_compiler_id(Buf **result) {...@@ -38,11 +38,19 @@ Error get_compiler_id(Buf **result) {
38 ZigList<Buf *> lib_paths = {};38 ZigList<Buf *> lib_paths = {};
39 if ((err = os_self_exe_shared_libs(lib_paths)))39 if ((err = os_self_exe_shared_libs(lib_paths)))
40 return err;40 return err;
41 #if defined(ZIG_OS_DARWIN)
42 // only add the self exe path on mac os
43 Buf *lib_path = lib_paths.at(0);
44 if ((err = cache_add_file(ch, lib_path)))
45 return err;
46 #else
41 for (size_t i = 0; i < lib_paths.length; i += 1) {47 for (size_t i = 0; i < lib_paths.length; i += 1) {
42 Buf *lib_path = lib_paths.at(i);48 Buf *lib_path = lib_paths.at(i);
43 if ((err = cache_add_file(ch, lib_path)))49 if ((err = cache_add_file(ch, lib_path)))
44 return err;50 return err;
45 }51 }
52 #endif
53
46 if ((err = cache_final(ch, &saved_compiler_id)))54 if ((err = cache_final(ch, &saved_compiler_id)))
47 return err;55 return err;
4856
src/hash_map.hpp+305-127
...@@ -19,45 +19,85 @@ public:...@@ -19,45 +19,85 @@ public:
19 init_capacity(capacity);19 init_capacity(capacity);
20 }20 }
21 void deinit(void) {21 void deinit(void) {
22 heap::c_allocator.deallocate(_entries, _capacity);22 _entries.deinit();
23 heap::c_allocator.deallocate(_index_bytes,
24 _indexes_len * capacity_index_size(_indexes_len));
23 }25 }
2426
25 struct Entry {27 struct Entry {
28 uint32_t hash;
29 uint32_t distance_from_start_index;
26 K key;30 K key;
27 V value;31 V value;
28 bool used;
29 int distance_from_start_index;
30 };32 };
3133
32 void clear() {34 void clear() {
33 for (int i = 0; i < _capacity; i += 1) {35 _entries.clear();
34 _entries[i].used = false;36 memset(_index_bytes, 0, _indexes_len * capacity_index_size(_indexes_len));
35 }
36 _size = 0;
37 _max_distance_from_start_index = 0;37 _max_distance_from_start_index = 0;
38 _modification_count += 1;38 _modification_count += 1;
39 }39 }
4040
41 int size() const {41 size_t size() const {
42 return _size;42 return _entries.length;
43 }43 }
4444
45 void put(const K &key, const V &value) {45 void put(const K &key, const V &value) {
46 _modification_count += 1;46 _modification_count += 1;
47 internal_put(key, value);47
4848 // This allows us to take a pointer to an entry in `internal_put` which
49 // if we get too full (60%), double the capacity49 // will not become a dead pointer when the array list is appended.
50 if (_size * 5 >= _capacity * 3) {50 _entries.ensure_capacity(_entries.length + 1);
51 Entry *old_entries = _entries;51
52 int old_capacity = _capacity;52 if (_index_bytes == nullptr) {
53 init_capacity(_capacity * 2);53 if (_entries.length < 16) {
54 // dump all of the old elements into the new table54 _entries.append({HashFunction(key), 0, key, value});
55 for (int i = 0; i < old_capacity; i += 1) {55 return;
56 Entry *old_entry = &old_entries[i];56 } else {
57 if (old_entry->used)57 _indexes_len = 32;
58 internal_put(old_entry->key, old_entry->value);58 _index_bytes = heap::c_allocator.allocate<uint8_t>(_indexes_len);
59 _max_distance_from_start_index = 0;
60 for (size_t i = 0; i < _entries.length; i += 1) {
61 Entry *entry = &_entries.items[i];
62 put_index(entry, i, _index_bytes);
63 }
64 return internal_put(key, value, _index_bytes);
65 }
66 }
67
68 // if we would get too full (60%), double the indexes size
69 if ((_entries.length + 1) * 5 >= _indexes_len * 3) {
70 heap::c_allocator.deallocate(_index_bytes,
71 _indexes_len * capacity_index_size(_indexes_len));
72 _indexes_len *= 2;
73 size_t sz = capacity_index_size(_indexes_len);
74 // This zero initializes the bytes, setting them all empty.
75 _index_bytes = heap::c_allocator.allocate<uint8_t>(_indexes_len * sz);
76 _max_distance_from_start_index = 0;
77 for (size_t i = 0; i < _entries.length; i += 1) {
78 Entry *entry = &_entries.items[i];
79 switch (sz) {
80 case 1:
81 put_index(entry, i, (uint8_t*)_index_bytes);
82 continue;
83 case 2:
84 put_index(entry, i, (uint16_t*)_index_bytes);
85 continue;
86 case 4:
87 put_index(entry, i, (uint32_t*)_index_bytes);
88 continue;
89 default:
90 put_index(entry, i, (size_t*)_index_bytes);
91 continue;
92 }
59 }93 }
60 heap::c_allocator.deallocate(old_entries, old_capacity);94 }
95
96 switch (capacity_index_size(_indexes_len)) {
97 case 1: return internal_put(key, value, (uint8_t*)_index_bytes);
98 case 2: return internal_put(key, value, (uint16_t*)_index_bytes);
99 case 4: return internal_put(key, value, (uint32_t*)_index_bytes);
100 default: return internal_put(key, value, (size_t*)_index_bytes);
61 }101 }
62 }102 }
63103
...@@ -81,40 +121,31 @@ public:...@@ -81,40 +121,31 @@ public:
81 return internal_get(key);121 return internal_get(key);
82 }122 }
83123
84 void maybe_remove(const K &key) {124 bool remove(const K &key) {
85 if (maybe_get(key)) {125 bool deleted_something = maybe_remove(key);
86 remove(key);126 if (!deleted_something)
87 }127 zig_panic("key not found");
128 return deleted_something;
88 }129 }
89130
90 void remove(const K &key) {131 bool maybe_remove(const K &key) {
91 _modification_count += 1;132 _modification_count += 1;
92 int start_index = key_to_index(key);133 if (_index_bytes == nullptr) {
93 for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {134 uint32_t hash = HashFunction(key);
94 int index = (start_index + roll_over) % _capacity;135 for (size_t i = 0; i < _entries.length; i += 1) {
95 Entry *entry = &_entries[index];136 if (_entries.items[i].hash == hash && EqualFn(_entries.items[i].key, key)) {
96137 _entries.swap_remove(i);
97 if (!entry->used)138 return true;
98 zig_panic("key not found");
99
100 if (!EqualFn(entry->key, key))
101 continue;
102
103 for (; roll_over < _capacity; roll_over += 1) {
104 int next_index = (start_index + roll_over + 1) % _capacity;
105 Entry *next_entry = &_entries[next_index];
106 if (!next_entry->used || next_entry->distance_from_start_index == 0) {
107 entry->used = false;
108 _size -= 1;
109 return;
110 }139 }
111 *entry = *next_entry;
112 entry->distance_from_start_index -= 1;
113 entry = next_entry;
114 }140 }
115 zig_panic("shifting everything in the table");141 return false;
142 }
143 switch (capacity_index_size(_indexes_len)) {
144 case 1: return internal_remove(key, (uint8_t*)_index_bytes);
145 case 2: return internal_remove(key, (uint16_t*)_index_bytes);
146 case 4: return internal_remove(key, (uint32_t*)_index_bytes);
147 default: return internal_remove(key, (size_t*)_index_bytes);
116 }148 }
117 zig_panic("key not found");
118 }149 }
119150
120 class Iterator {151 class Iterator {
...@@ -122,24 +153,16 @@ public:...@@ -122,24 +153,16 @@ public:
122 Entry *next() {153 Entry *next() {
123 if (_inital_modification_count != _table->_modification_count)154 if (_inital_modification_count != _table->_modification_count)
124 zig_panic("concurrent modification");155 zig_panic("concurrent modification");
125 if (_count >= _table->size())156 if (_index >= _table->_entries.length)
126 return NULL;157 return nullptr;
127 for (; _index < _table->_capacity; _index += 1) {158 Entry *entry = &_table->_entries.items[_index];
128 Entry *entry = &_table->_entries[_index];159 _index += 1;
129 if (entry->used) {160 return entry;
130 _index += 1;
131 _count += 1;
132 return entry;
133 }
134 }
135 zig_panic("no next item");
136 }161 }
137 private:162 private:
138 const HashMap * _table;163 const HashMap * _table;
139 // how many items have we returned
140 int _count = 0;
141 // iterator through the entry array164 // iterator through the entry array
142 int _index = 0;165 size_t _index = 0;
143 // used to detect concurrent modification166 // used to detect concurrent modification
144 uint32_t _inital_modification_count;167 uint32_t _inital_modification_count;
145 Iterator(const HashMap * table) :168 Iterator(const HashMap * table) :
...@@ -154,89 +177,244 @@ public:...@@ -154,89 +177,244 @@ public:
154 }177 }
155178
156private:179private:
157180 // Maintains insertion order.
158 Entry *_entries;181 ZigList<Entry> _entries;
159 int _capacity;182 // If _indexes_len is less than 2**8, this is an array of uint8_t.
160 int _size;183 // If _indexes_len is less than 2**16, it is an array of uint16_t.
161 int _max_distance_from_start_index;184 // If _indexes_len is less than 2**32, it is an array of uint32_t.
162 // this is used to detect bugs where a hashtable is edited while an iterator is running.185 // Otherwise it is size_t.
186 // It's off by 1. 0 means empty slot, 1 means index 0, etc.
187 uint8_t *_index_bytes;
188 // This is the number of indexes. When indexes are bytes, it equals number of bytes.
189 // When indexes are uint16_t, _indexes_len is half the number of bytes.
190 size_t _indexes_len;
191
192 size_t _max_distance_from_start_index;
193 // This is used to detect bugs where a hashtable is edited while an iterator is running.
163 uint32_t _modification_count;194 uint32_t _modification_count;
164195
165 void init_capacity(int capacity) {196 void init_capacity(size_t capacity) {
166 _capacity = capacity;197 _entries = {};
167 _entries = heap::c_allocator.allocate<Entry>(_capacity);198 _entries.ensure_capacity(capacity);
168 _size = 0;199 _indexes_len = 0;
169 _max_distance_from_start_index = 0;200 if (capacity >= 16) {
170 for (int i = 0; i < _capacity; i += 1) {201 // So that at capacity it will only be 60% full.
171 _entries[i].used = false;202 _indexes_len = capacity * 5 / 3;
203 size_t sz = capacity_index_size(_indexes_len);
204 // This zero initializes _index_bytes which sets them all to empty.
205 _index_bytes = heap::c_allocator.allocate<uint8_t>(_indexes_len * sz);
206 } else {
207 _index_bytes = nullptr;
172 }208 }
209
210 _max_distance_from_start_index = 0;
211 _modification_count = 0;
173 }212 }
174213
175 void internal_put(K key, V value) {214 static size_t capacity_index_size(size_t len) {
176 int start_index = key_to_index(key);215 if (len < UINT8_MAX)
177 for (int roll_over = 0, distance_from_start_index = 0;216 return 1;
178 roll_over < _capacity; roll_over += 1, distance_from_start_index += 1)217 if (len < UINT16_MAX)
218 return 2;
219 if (len < UINT32_MAX)
220 return 4;
221 return sizeof(size_t);
222 }
223
224 template <typename I>
225 void internal_put(const K &key, const V &value, I *indexes) {
226 uint32_t hash = HashFunction(key);
227 uint32_t distance_from_start_index = 0;
228 size_t start_index = hash_to_index(hash);
229 for (size_t roll_over = 0; roll_over < _indexes_len;
230 roll_over += 1, distance_from_start_index += 1)
179 {231 {
180 int index = (start_index + roll_over) % _capacity;232 size_t index_index = (start_index + roll_over) % _indexes_len;
181 Entry *entry = &_entries[index];233 I index_data = indexes[index_index];
182234 if (index_data == 0) {
183 if (entry->used && !EqualFn(entry->key, key)) {235 _entries.append_assuming_capacity({ hash, distance_from_start_index, key, value });
184 if (entry->distance_from_start_index < distance_from_start_index) {236 indexes[index_index] = _entries.length;
185 // robin hood to the rescue237 if (distance_from_start_index > _max_distance_from_start_index)
186 Entry tmp = *entry;238 _max_distance_from_start_index = distance_from_start_index;
187 if (distance_from_start_index > _max_distance_from_start_index)239 return;
188 _max_distance_from_start_index = distance_from_start_index;240 }
189 *entry = {241 // This pointer survives the following append because we call
190 key,242 // _entries.ensure_capacity before internal_put.
191 value,243 Entry *entry = &_entries.items[index_data - 1];
192 true,244 if (entry->hash == hash && EqualFn(entry->key, key)) {
193 distance_from_start_index,245 *entry = {hash, distance_from_start_index, key, value};
194 };246 if (distance_from_start_index > _max_distance_from_start_index)
195 key = tmp.key;247 _max_distance_from_start_index = distance_from_start_index;
196 value = tmp.value;248 return;
197 distance_from_start_index = tmp.distance_from_start_index;249 }
250 if (entry->distance_from_start_index < distance_from_start_index) {
251 // In this case, we did not find the item. We will put a new entry.
252 // However, we will use this index for the new entry, and move
253 // the previous index down the line, to keep the _max_distance_from_start_index
254 // as small as possible.
255 _entries.append_assuming_capacity({ hash, distance_from_start_index, key, value });
256 indexes[index_index] = _entries.length;
257 if (distance_from_start_index > _max_distance_from_start_index)
258 _max_distance_from_start_index = distance_from_start_index;
259
260 distance_from_start_index = entry->distance_from_start_index;
261
262 // Find somewhere to put the index we replaced by shifting
263 // following indexes backwards.
264 roll_over += 1;
265 distance_from_start_index += 1;
266 for (; roll_over < _indexes_len; roll_over += 1, distance_from_start_index += 1) {
267 size_t index_index = (start_index + roll_over) % _indexes_len;
268 I next_index_data = indexes[index_index];
269 if (next_index_data == 0) {
270 if (distance_from_start_index > _max_distance_from_start_index)
271 _max_distance_from_start_index = distance_from_start_index;
272 entry->distance_from_start_index = distance_from_start_index;
273 indexes[index_index] = index_data;
274 return;
275 }
276 Entry *next_entry = &_entries.items[next_index_data - 1];
277 if (next_entry->distance_from_start_index < distance_from_start_index) {
278 if (distance_from_start_index > _max_distance_from_start_index)
279 _max_distance_from_start_index = distance_from_start_index;
280 entry->distance_from_start_index = distance_from_start_index;
281 indexes[index_index] = index_data;
282 distance_from_start_index = next_entry->distance_from_start_index;
283 entry = next_entry;
284 index_data = next_index_data;
285 }
198 }286 }
199 continue;287 zig_unreachable();
288 }
289 }
290 zig_unreachable();
291 }
292
293 template <typename I>
294 void put_index(Entry *entry, size_t entry_index, I *indexes) {
295 size_t start_index = hash_to_index(entry->hash);
296 size_t index_data = entry_index + 1;
297 for (size_t roll_over = 0, distance_from_start_index = 0;
298 roll_over < _indexes_len; roll_over += 1, distance_from_start_index += 1)
299 {
300 size_t index_index = (start_index + roll_over) % _indexes_len;
301 size_t next_index_data = indexes[index_index];
302 if (next_index_data == 0) {
303 if (distance_from_start_index > _max_distance_from_start_index)
304 _max_distance_from_start_index = distance_from_start_index;
305 entry->distance_from_start_index = distance_from_start_index;
306 indexes[index_index] = index_data;
307 return;
308 }
309 Entry *next_entry = &_entries.items[next_index_data - 1];
310 if (next_entry->distance_from_start_index < distance_from_start_index) {
311 if (distance_from_start_index > _max_distance_from_start_index)
312 _max_distance_from_start_index = distance_from_start_index;
313 entry->distance_from_start_index = distance_from_start_index;
314 indexes[index_index] = index_data;
315 distance_from_start_index = next_entry->distance_from_start_index;
316 entry = next_entry;
317 index_data = next_index_data;
200 }318 }
319 }
320 zig_unreachable();
321 }
201322
202 if (!entry->used) {323 Entry *internal_get(const K &key) const {
203 // adding an entry. otherwise overwriting old value with324 if (_index_bytes == nullptr) {
204 // same key325 uint32_t hash = HashFunction(key);
205 _size += 1;326 for (size_t i = 0; i < _entries.length; i += 1) {
327 if (_entries.items[i].hash == hash && EqualFn(_entries.items[i].key, key)) {
328 return &_entries.items[i];
329 }
206 }330 }
331 return nullptr;
332 }
333 switch (capacity_index_size(_indexes_len)) {
334 case 1: return internal_get2(key, (uint8_t*)_index_bytes);
335 case 2: return internal_get2(key, (uint16_t*)_index_bytes);
336 case 4: return internal_get2(key, (uint32_t*)_index_bytes);
337 default: return internal_get2(key, (size_t*)_index_bytes);
338 }
339 }
207340
208 if (distance_from_start_index > _max_distance_from_start_index)341 template <typename I>
209 _max_distance_from_start_index = distance_from_start_index;342 Entry *internal_get2(const K &key, I *indexes) const {
210 *entry = {343 uint32_t hash = HashFunction(key);
211 key,344 size_t start_index = hash_to_index(hash);
212 value,345 for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
213 true,346 size_t index_index = (start_index + roll_over) % _indexes_len;
214 distance_from_start_index,347 size_t index_data = indexes[index_index];
215 };348 if (index_data == 0)
216 return;349 return nullptr;
350
351 Entry *entry = &_entries.items[index_data - 1];
352 if (entry->hash == hash && EqualFn(entry->key, key))
353 return entry;
217 }354 }
218 zig_panic("put into a full HashMap");355 return nullptr;
219 }356 }
220357
358 size_t hash_to_index(uint32_t hash) const {
359 return ((size_t)hash) % _indexes_len;
360 }
221361
222 Entry *internal_get(const K &key) const {362 template <typename I>
223 int start_index = key_to_index(key);363 bool internal_remove(const K &key, I *indexes) {
224 for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {364 uint32_t hash = HashFunction(key);
225 int index = (start_index + roll_over) % _capacity;365 size_t start_index = hash_to_index(hash);
226 Entry *entry = &_entries[index];366 for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
367 size_t index_index = (start_index + roll_over) % _indexes_len;
368 size_t index_data = indexes[index_index];
369 if (index_data == 0)
370 return false;
371
372 size_t index = index_data - 1;
373 Entry *entry = &_entries.items[index];
374 if (entry->hash != hash || !EqualFn(entry->key, key))
375 continue;
227376
228 if (!entry->used)377 size_t prev_index = index_index;
229 return NULL;378 _entries.swap_remove(index);
379 if (_entries.length > 0 && _entries.length != index) {
380 // Because of the swap remove, now we need to update the index that was
381 // pointing to the last entry and is now pointing to this removed item slot.
382 update_entry_index(_entries.length, index, indexes);
383 }
230384
231 if (EqualFn(entry->key, key))385 // Now we have to shift over the following indexes.
232 return entry;386 roll_over += 1;
387 for (; roll_over < _indexes_len; roll_over += 1) {
388 size_t next_index = (start_index + roll_over) % _indexes_len;
389 if (indexes[next_index] == 0) {
390 indexes[prev_index] = 0;
391 return true;
392 }
393 Entry *next_entry = &_entries.items[indexes[next_index] - 1];
394 if (next_entry->distance_from_start_index == 0) {
395 indexes[prev_index] = 0;
396 return true;
397 }
398 indexes[prev_index] = indexes[next_index];
399 prev_index = next_index;
400 next_entry->distance_from_start_index -= 1;
401 }
402 zig_unreachable();
233 }403 }
234 return NULL;404 return false;
235 }405 }
236406
237 int key_to_index(const K &key) const {407 template <typename I>
238 return (int)(HashFunction(key) % ((uint32_t)_capacity));408 void update_entry_index(size_t old_entry_index, size_t new_entry_index, I *indexes) {
409 size_t start_index = hash_to_index(_entries.items[new_entry_index].hash);
410 for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) {
411 size_t index_index = (start_index + roll_over) % _indexes_len;
412 if (indexes[index_index] == old_entry_index + 1) {
413 indexes[index_index] = new_entry_index + 1;
414 return;
415 }
416 }
417 zig_unreachable();
239 }418 }
240};419};
241
242#endif420#endif
src/ir.cpp+584-144
...@@ -13,6 +13,7 @@...@@ -13,6 +13,7 @@
13#include "os.hpp"13#include "os.hpp"
14#include "range_set.hpp"14#include "range_set.hpp"
15#include "softfloat.hpp"15#include "softfloat.hpp"
16#include "softfloat_ext.hpp"
16#include "util.hpp"17#include "util.hpp"
17#include "mem_list.hpp"18#include "mem_list.hpp"
18#include "all_types.hpp"19#include "all_types.hpp"
...@@ -286,6 +287,7 @@ static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* so...@@ -286,6 +287,7 @@ static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* so
286 IrInstGen *struct_operand, TypeStructField *field);287 IrInstGen *struct_operand, TypeStructField *field);
287static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right);288static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right);
288static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right);289static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right);
290static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field);
289291
290#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)292#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
291#define ir_assert_gen(OK, SOURCE_INSTRUCTION) ir_assert_gen_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)293#define ir_assert_gen(OK, SOURCE_INSTRUCTION) ir_assert_gen_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
...@@ -308,6 +310,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {...@@ -308,6 +310,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
308 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCall *>(inst));310 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCall *>(inst));
309 case IrInstSrcIdCallExtra:311 case IrInstSrcIdCallExtra:
310 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallExtra *>(inst));312 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallExtra *>(inst));
313 case IrInstSrcIdAsyncCallExtra:
314 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAsyncCallExtra *>(inst));
311 case IrInstSrcIdUnOp:315 case IrInstSrcIdUnOp:
312 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnOp *>(inst));316 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnOp *>(inst));
313 case IrInstSrcIdCondBr:317 case IrInstSrcIdCondBr:
...@@ -560,6 +564,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {...@@ -560,6 +564,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
560 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemorySize *>(inst));564 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemorySize *>(inst));
561 case IrInstSrcIdWasmMemoryGrow:565 case IrInstSrcIdWasmMemoryGrow:
562 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemoryGrow *>(inst));566 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemoryGrow *>(inst));
567 case IrInstSrcIdSrc:
568 return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSrc *>(inst));
563 }569 }
564 zig_unreachable();570 zig_unreachable();
565}571}
...@@ -822,12 +828,11 @@ static ZigValue *const_ptr_pointee_unchecked_no_isf(CodeGen *g, ZigValue *const_...@@ -822,12 +828,11 @@ static ZigValue *const_ptr_pointee_unchecked_no_isf(CodeGen *g, ZigValue *const_
822 ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val;828 ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val;
823 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;829 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
824830
825 // TODO handle sentinel terminated arrays
826 expand_undef_array(g, array_val);831 expand_undef_array(g, array_val);
827 result = g->pass1_arena->create<ZigValue>();832 result = g->pass1_arena->create<ZigValue>();
828 result->special = array_val->special;833 result->special = array_val->special;
829 result->type = get_array_type(g, array_val->type->data.array.child_type,834 result->type = get_array_type(g, array_val->type->data.array.child_type,
830 array_val->type->data.array.len - elem_index, nullptr);835 array_val->type->data.array.len - elem_index, array_val->type->data.array.sentinel);
831 result->data.x_array.special = ConstArraySpecialNone;836 result->data.x_array.special = ConstArraySpecialNone;
832 result->data.x_array.data.s_none.elements = &array_val->data.x_array.data.s_none.elements[elem_index];837 result->data.x_array.data.s_none.elements = &array_val->data.x_array.data.s_none.elements[elem_index];
833 result->parent.id = ConstParentIdArray;838 result->parent.id = ConstParentIdArray;
...@@ -1170,6 +1175,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallExtra *) {...@@ -1170,6 +1175,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallExtra *) {
1170 return IrInstSrcIdCallExtra;1175 return IrInstSrcIdCallExtra;
1171}1176}
11721177
1178static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsyncCallExtra *) {
1179 return IrInstSrcIdAsyncCallExtra;
1180}
1181
1173static constexpr IrInstSrcId ir_inst_id(IrInstSrcConst *) {1182static constexpr IrInstSrcId ir_inst_id(IrInstSrcConst *) {
1174 return IrInstSrcIdConst;1183 return IrInstSrcIdConst;
1175}1184}
...@@ -1626,6 +1635,9 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemoryGrow *) {...@@ -1626,6 +1635,9 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemoryGrow *) {
1626 return IrInstSrcIdWasmMemoryGrow;1635 return IrInstSrcIdWasmMemoryGrow;
1627}1636}
16281637
1638static constexpr IrInstSrcId ir_inst_id(IrInstSrcSrc *) {
1639 return IrInstSrcIdSrc;
1640}
16291641
1630static constexpr IrInstGenId ir_inst_id(IrInstGenDeclVar *) {1642static constexpr IrInstGenId ir_inst_id(IrInstGenDeclVar *) {
1631 return IrInstGenIdDeclVar;1643 return IrInstGenIdDeclVar;
...@@ -2436,6 +2448,25 @@ static IrInstSrc *ir_build_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *...@@ -2436,6 +2448,25 @@ static IrInstSrc *ir_build_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *
2436 return &call_instruction->base;2448 return &call_instruction->base;
2437}2449}
24382450
2451static IrInstSrc *ir_build_async_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
2452 CallModifier modifier, IrInstSrc *fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstSrc *args, ResultLoc *result_loc)
2453{
2454 IrInstSrcAsyncCallExtra *call_instruction = ir_build_instruction<IrInstSrcAsyncCallExtra>(irb, scope, source_node);
2455 call_instruction->modifier = modifier;
2456 call_instruction->fn_ref = fn_ref;
2457 call_instruction->ret_ptr = ret_ptr;
2458 call_instruction->new_stack = new_stack;
2459 call_instruction->args = args;
2460 call_instruction->result_loc = result_loc;
2461
2462 ir_ref_instruction(fn_ref, irb->current_basic_block);
2463 if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block);
2464 ir_ref_instruction(new_stack, irb->current_basic_block);
2465 ir_ref_instruction(args, irb->current_basic_block);
2466
2467 return &call_instruction->base;
2468}
2469
2439static IrInstSrc *ir_build_call_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,2470static IrInstSrc *ir_build_call_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
2440 IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc **args_ptr, size_t args_len,2471 IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc **args_ptr, size_t args_len,
2441 ResultLoc *result_loc)2472 ResultLoc *result_loc)
...@@ -5029,6 +5060,11 @@ static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_i...@@ -5029,6 +5060,11 @@ static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_i
5029 return &instruction->base;5060 return &instruction->base;
5030}5061}
50315062
5063static IrInstSrc *ir_build_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
5064 IrInstSrcSrc *instruction = ir_build_instruction<IrInstSrcSrc>(irb, scope, source_node);
5065
5066 return &instruction->base;
5067}
50325068
5033static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {5069static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
5034 results[ReturnKindUnconditional] = 0;5070 results[ReturnKindUnconditional] = 0;
...@@ -6172,11 +6208,10 @@ static IrInstSrc *ir_gen_this(IrBuilderSrc *irb, Scope *orig_scope, AstNode *nod...@@ -6172,11 +6208,10 @@ static IrInstSrc *ir_gen_this(IrBuilderSrc *irb, Scope *orig_scope, AstNode *nod
6172static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *await_node, AstNode *call_node,6208static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *await_node, AstNode *call_node,
6173 LVal lval, ResultLoc *result_loc)6209 LVal lval, ResultLoc *result_loc)
6174{6210{
6175 size_t arg_offset = 3;6211 if (call_node->data.fn_call_expr.params.length != 4) {
6176 if (call_node->data.fn_call_expr.params.length < arg_offset) {
6177 add_node_error(irb->codegen, call_node,6212 add_node_error(irb->codegen, call_node,
6178 buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,6213 buf_sprintf("expected 4 arguments, found %" ZIG_PRI_usize,
6179 arg_offset, call_node->data.fn_call_expr.params.length));6214 call_node->data.fn_call_expr.params.length));
6180 return irb->codegen->invalid_inst_src;6215 return irb->codegen->invalid_inst_src;
6181 }6216 }
61826217
...@@ -6195,20 +6230,37 @@ static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *aw...@@ -6195,20 +6230,37 @@ static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *aw
6195 if (fn_ref == irb->codegen->invalid_inst_src)6230 if (fn_ref == irb->codegen->invalid_inst_src)
6196 return fn_ref;6231 return fn_ref;
61976232
6198 size_t arg_count = call_node->data.fn_call_expr.params.length - arg_offset;
6199 IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
6200 for (size_t i = 0; i < arg_count; i += 1) {
6201 AstNode *arg_node = call_node->data.fn_call_expr.params.at(i + arg_offset);
6202 IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
6203 if (arg == irb->codegen->invalid_inst_src)
6204 return arg;
6205 args[i] = arg;
6206 }
6207
6208 CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone;6233 CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone;
6209 bool is_async_call_builtin = true;6234 bool is_async_call_builtin = true;
6210 IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args,6235 AstNode *args_node = call_node->data.fn_call_expr.params.at(3);
6211 ret_ptr, modifier, is_async_call_builtin, bytes, result_loc);6236 if (args_node->type == NodeTypeContainerInitExpr) {
6237 if (args_node->data.container_init_expr.kind == ContainerInitKindArray ||
6238 args_node->data.container_init_expr.entries.length == 0)
6239 {
6240 size_t arg_count = args_node->data.container_init_expr.entries.length;
6241 IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
6242 for (size_t i = 0; i < arg_count; i += 1) {
6243 AstNode *arg_node = args_node->data.container_init_expr.entries.at(i);
6244 IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
6245 if (arg == irb->codegen->invalid_inst_src)
6246 return arg;
6247 args[i] = arg;
6248 }
6249
6250 IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args,
6251 ret_ptr, modifier, is_async_call_builtin, bytes, result_loc);
6252 return ir_lval_wrap(irb, scope, call, lval, result_loc);
6253 } else {
6254 exec_add_error_node(irb->codegen, irb->exec, args_node,
6255 buf_sprintf("TODO: @asyncCall with anon struct literal"));
6256 return irb->codegen->invalid_inst_src;
6257 }
6258 }
6259 IrInstSrc *args = ir_gen_node(irb, args_node, scope);
6260 if (args == irb->codegen->invalid_inst_src)
6261 return args;
6262
6263 IrInstSrc *call = ir_build_async_call_extra(irb, scope, call_node, modifier, fn_ref, ret_ptr, bytes, args, result_loc);
6212 return ir_lval_wrap(irb, scope, call, lval, result_loc);6264 return ir_lval_wrap(irb, scope, call, lval, result_loc);
6213}6265}
62146266
...@@ -7449,6 +7501,11 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod...@@ -7449,6 +7501,11 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
7449 return ir_gen_union_init_expr(irb, scope, node, union_type_inst, name_inst, init_node,7501 return ir_gen_union_init_expr(irb, scope, node, union_type_inst, name_inst, init_node,
7450 lval, result_loc);7502 lval, result_loc);
7451 }7503 }
7504 case BuiltinFnIdSrc:
7505 {
7506 IrInstSrc *src_inst = ir_build_src(irb, scope, node);
7507 return ir_lval_wrap(irb, scope, src_inst, lval, result_loc);
7508 }
7452 }7509 }
7453 zig_unreachable();7510 zig_unreachable();
7454}7511}
...@@ -9885,7 +9942,7 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod...@@ -9885,7 +9942,7 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod
9885 is_var_args = true;9942 is_var_args = true;
9886 break;9943 break;
9887 }9944 }
9888 if (param_node->data.param_decl.var_token == nullptr) {9945 if (param_node->data.param_decl.anytype_token == nullptr) {
9889 AstNode *type_node = param_node->data.param_decl.type;9946 AstNode *type_node = param_node->data.param_decl.type;
9890 IrInstSrc *type_value = ir_gen_node(irb, type_node, parent_scope);9947 IrInstSrc *type_value = ir_gen_node(irb, type_node, parent_scope);
9891 if (type_value == irb->codegen->invalid_inst_src)9948 if (type_value == irb->codegen->invalid_inst_src)
...@@ -9911,7 +9968,7 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod...@@ -9911,7 +9968,7 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod
9911 }9968 }
99129969
9913 IrInstSrc *return_type;9970 IrInstSrc *return_type;
9914 if (node->data.fn_proto.return_var_token == nullptr) {9971 if (node->data.fn_proto.return_anytype_token == nullptr) {
9915 if (node->data.fn_proto.return_type == nullptr) {9972 if (node->data.fn_proto.return_type == nullptr) {
9916 return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void);9973 return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void);
9917 } else {9974 } else {
...@@ -10169,9 +10226,9 @@ static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope...@@ -10169,9 +10226,9 @@ static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope
10169 add_node_error(irb->codegen, node,10226 add_node_error(irb->codegen, node,
10170 buf_sprintf("inferred array size invalid here"));10227 buf_sprintf("inferred array size invalid here"));
10171 return irb->codegen->invalid_inst_src;10228 return irb->codegen->invalid_inst_src;
10172 case NodeTypeVarFieldType:10229 case NodeTypeAnyTypeField:
10173 return ir_lval_wrap(irb, scope,10230 return ir_lval_wrap(irb, scope,
10174 ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var), lval, result_loc);10231 ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_anytype), lval, result_loc);
10175 }10232 }
10176 zig_unreachable();10233 zig_unreachable();
10177}10234}
...@@ -10239,7 +10296,7 @@ static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *sco...@@ -10239,7 +10296,7 @@ static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *sco
10239 case NodeTypeSuspend:10296 case NodeTypeSuspend:
10240 case NodeTypeEnumLiteral:10297 case NodeTypeEnumLiteral:
10241 case NodeTypeInferredArrayType:10298 case NodeTypeInferredArrayType:
10242 case NodeTypeVarFieldType:10299 case NodeTypeAnyTypeField:
10243 case NodeTypePrefixOpExpr:10300 case NodeTypePrefixOpExpr:
10244 add_node_error(irb->codegen, node,10301 add_node_error(irb->codegen, node,
10245 buf_sprintf("invalid left-hand side to assignment"));10302 buf_sprintf("invalid left-hand side to assignment"));
...@@ -10461,7 +10518,7 @@ ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_va...@@ -10461,7 +10518,7 @@ ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_va
10461 if (val == nullptr) return nullptr;10518 if (val == nullptr) return nullptr;
10462 assert(const_val->type->id == ZigTypeIdPointer);10519 assert(const_val->type->id == ZigTypeIdPointer);
10463 ZigType *expected_type = const_val->type->data.pointer.child_type;10520 ZigType *expected_type = const_val->type->data.pointer.child_type;
10464 if (expected_type == codegen->builtin_types.entry_var) {10521 if (expected_type == codegen->builtin_types.entry_anytype) {
10465 return val;10522 return val;
10466 }10523 }
10467 switch (type_has_one_possible_value(codegen, expected_type)) {10524 switch (type_has_one_possible_value(codegen, expected_type)) {
...@@ -12585,28 +12642,29 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT...@@ -12585,28 +12642,29 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
12585 if (prev_type->id == ZigTypeIdPointer &&12642 if (prev_type->id == ZigTypeIdPointer &&
12586 prev_type->data.pointer.ptr_len == PtrLenSingle &&12643 prev_type->data.pointer.ptr_len == PtrLenSingle &&
12587 prev_type->data.pointer.child_type->id == ZigTypeIdArray &&12644 prev_type->data.pointer.child_type->id == ZigTypeIdArray &&
12588 ((cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenUnknown))) 12645 ((cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenUnknown)))
12589 {12646 {
12590 prev_inst = cur_inst; 12647 convert_to_const_slice = false;
12648 prev_inst = cur_inst;
1259112649
12592 if (prev_type->data.pointer.is_const && !cur_type->data.pointer.is_const) {12650 if (prev_type->data.pointer.is_const && !cur_type->data.pointer.is_const) {
12593 // const array pointer and non-const unknown pointer12651 // const array pointer and non-const unknown pointer
12594 make_the_pointer_const = true;12652 make_the_pointer_const = true;
12595 }12653 }
12596 continue; 12654 continue;
12597 }12655 }
1259812656
12599 // *[N]T to [*]T12657 // *[N]T to [*]T
12600 if (cur_type->id == ZigTypeIdPointer &&12658 if (cur_type->id == ZigTypeIdPointer &&
12601 cur_type->data.pointer.ptr_len == PtrLenSingle &&12659 cur_type->data.pointer.ptr_len == PtrLenSingle &&
12602 cur_type->data.pointer.child_type->id == ZigTypeIdArray &&12660 cur_type->data.pointer.child_type->id == ZigTypeIdArray &&
12603 ((prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenUnknown))) 12661 ((prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenUnknown)))
12604 {12662 {
12605 if (cur_type->data.pointer.is_const && !prev_type->data.pointer.is_const) {12663 if (cur_type->data.pointer.is_const && !prev_type->data.pointer.is_const) {
12606 // const array pointer and non-const unknown pointer12664 // const array pointer and non-const unknown pointer
12607 make_the_pointer_const = true;12665 make_the_pointer_const = true;
12608 }12666 }
12609 continue; 12667 continue;
12610 }12668 }
1261112669
12612 // *[N]T to []T12670 // *[N]T to []T
...@@ -12962,7 +13020,11 @@ static IrInstGen *ir_resolve_cast(IrAnalyze *ira, IrInst *source_instr, IrInstGe...@@ -12962,7 +13020,11 @@ static IrInstGen *ir_resolve_cast(IrAnalyze *ira, IrInst *source_instr, IrInstGe
12962{13020{
12963 if (instr_is_comptime(value) || !type_has_bits(ira->codegen, wanted_type)) {13021 if (instr_is_comptime(value) || !type_has_bits(ira->codegen, wanted_type)) {
12964 IrInstGen *result = ir_const(ira, source_instr, wanted_type);13022 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
12965 if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, value->value, value->value->type,13023 ZigValue *val = ir_resolve_const(ira, value, UndefBad);
13024 if (val == nullptr)
13025 return ira->codegen->invalid_inst_gen;
13026
13027 if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, val, val->type,
12966 result->value, wanted_type))13028 result->value, wanted_type))
12967 {13029 {
12968 return ira->codegen->invalid_inst_gen;13030 return ira->codegen->invalid_inst_gen;
...@@ -14703,10 +14765,139 @@ static IrInstGen *ir_analyze_struct_literal_to_array(IrAnalyze *ira, IrInst* sou...@@ -14703,10 +14765,139 @@ static IrInstGen *ir_analyze_struct_literal_to_array(IrAnalyze *ira, IrInst* sou
14703}14765}
1470414766
14705static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* source_instr,14767static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* source_instr,
14706 IrInstGen *value, ZigType *wanted_type)14768 IrInstGen *struct_operand, ZigType *wanted_type)
14707{14769{
14708 ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon struct literal to struct"));14770 Error err;
14709 return ira->codegen->invalid_inst_gen;14771
14772 IrInstGen *struct_ptr = ir_get_ref(ira, source_instr, struct_operand, true, false);
14773 if (type_is_invalid(struct_ptr->value->type))
14774 return ira->codegen->invalid_inst_gen;
14775
14776 if (wanted_type->data.structure.resolve_status == ResolveStatusBeingInferred) {
14777 ir_add_error(ira, source_instr, buf_sprintf("type coercion of anon struct literal to inferred struct"));
14778 return ira->codegen->invalid_inst_gen;
14779 }
14780
14781 if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusSizeKnown)))
14782 return ira->codegen->invalid_inst_gen;
14783
14784 size_t actual_field_count = wanted_type->data.structure.src_field_count;
14785 size_t instr_field_count = struct_operand->value->type->data.structure.src_field_count;
14786
14787 bool need_comptime = ir_should_inline(ira->old_irb.exec, source_instr->scope)
14788 || type_requires_comptime(ira->codegen, wanted_type) == ReqCompTimeYes;
14789 bool is_comptime = true;
14790
14791 // Determine if the struct_operand will be comptime.
14792 // Also emit compile errors for missing fields and duplicate fields.
14793 AstNode **field_assign_nodes = heap::c_allocator.allocate<AstNode *>(actual_field_count);
14794 ZigValue **field_values = heap::c_allocator.allocate<ZigValue *>(actual_field_count);
14795 IrInstGen **casted_fields = heap::c_allocator.allocate<IrInstGen *>(actual_field_count);
14796 IrInstGen *const_result = ir_const(ira, source_instr, wanted_type);
14797
14798 for (size_t i = 0; i < instr_field_count; i += 1) {
14799 TypeStructField *src_field = struct_operand->value->type->data.structure.fields[i];
14800 TypeStructField *dst_field = find_struct_type_field(wanted_type, src_field->name);
14801 if (dst_field == nullptr) {
14802 ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("no field named '%s' in struct '%s'",
14803 buf_ptr(src_field->name), buf_ptr(&wanted_type->name)));
14804 if (wanted_type->data.structure.decl_node) {
14805 add_error_note(ira->codegen, msg, wanted_type->data.structure.decl_node,
14806 buf_sprintf("struct '%s' declared here", buf_ptr(&wanted_type->name)));
14807 }
14808 add_error_note(ira->codegen, msg, src_field->decl_node,
14809 buf_sprintf("field '%s' declared here", buf_ptr(src_field->name)));
14810 return ira->codegen->invalid_inst_gen;
14811 }
14812
14813 ir_assert(src_field->decl_node != nullptr, source_instr);
14814 AstNode *existing_assign_node = field_assign_nodes[dst_field->src_index];
14815 if (existing_assign_node != nullptr) {
14816 ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("duplicate field"));
14817 add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here"));
14818 return ira->codegen->invalid_inst_gen;
14819 }
14820 field_assign_nodes[dst_field->src_index] = src_field->decl_node;
14821
14822 IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, src_field, struct_ptr,
14823 struct_operand->value->type, false);
14824 if (type_is_invalid(field_ptr->value->type))
14825 return ira->codegen->invalid_inst_gen;
14826 IrInstGen *field_value = ir_get_deref(ira, source_instr, field_ptr, nullptr);
14827 if (type_is_invalid(field_value->value->type))
14828 return ira->codegen->invalid_inst_gen;
14829 IrInstGen *casted_value = ir_implicit_cast(ira, field_value, dst_field->type_entry);
14830 if (type_is_invalid(casted_value->value->type))
14831 return ira->codegen->invalid_inst_gen;
14832
14833 casted_fields[dst_field->src_index] = casted_value;
14834 if (need_comptime || instr_is_comptime(casted_value)) {
14835 ZigValue *field_val = ir_resolve_const(ira, casted_value, UndefOk);
14836 if (field_val == nullptr)
14837 return ira->codegen->invalid_inst_gen;
14838 field_val->parent.id = ConstParentIdStruct;
14839 field_val->parent.data.p_struct.struct_val = const_result->value;
14840 field_val->parent.data.p_struct.field_index = dst_field->src_index;
14841 field_values[dst_field->src_index] = field_val;
14842 } else {
14843 is_comptime = false;
14844 }
14845 }
14846
14847 bool any_missing = false;
14848 for (size_t i = 0; i < actual_field_count; i += 1) {
14849 if (field_assign_nodes[i] != nullptr) continue;
14850
14851 // look for a default field value
14852 TypeStructField *field = wanted_type->data.structure.fields[i];
14853 memoize_field_init_val(ira->codegen, wanted_type, field);
14854 if (field->init_val == nullptr) {
14855 ir_add_error(ira, source_instr,
14856 buf_sprintf("missing field: '%s'", buf_ptr(field->name)));
14857 any_missing = true;
14858 continue;
14859 }
14860 if (type_is_invalid(field->init_val->type))
14861 return ira->codegen->invalid_inst_gen;
14862 ZigValue *init_val_copy = ira->codegen->pass1_arena->create<ZigValue>();
14863 copy_const_val(ira->codegen, init_val_copy, field->init_val);
14864 init_val_copy->parent.id = ConstParentIdStruct;
14865 init_val_copy->parent.data.p_struct.struct_val = const_result->value;
14866 init_val_copy->parent.data.p_struct.field_index = i;
14867 field_values[i] = init_val_copy;
14868 casted_fields[i] = ir_const_move(ira, source_instr, init_val_copy);
14869 }
14870 if (any_missing)
14871 return ira->codegen->invalid_inst_gen;
14872
14873 if (is_comptime) {
14874 heap::c_allocator.deallocate(field_assign_nodes, actual_field_count);
14875 IrInstGen *const_result = ir_const(ira, source_instr, wanted_type);
14876 const_result->value->data.x_struct.fields = field_values;
14877 return const_result;
14878 }
14879
14880 IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, no_result_loc(),
14881 wanted_type, nullptr, true, true);
14882 if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) {
14883 return ira->codegen->invalid_inst_gen;
14884 }
14885
14886 for (size_t i = 0; i < actual_field_count; i += 1) {
14887 TypeStructField *field = wanted_type->data.structure.fields[i];
14888 IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, result_loc_inst, wanted_type, true);
14889 if (type_is_invalid(field_ptr->value->type))
14890 return ira->codegen->invalid_inst_gen;
14891 IrInstGen *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, field_ptr, casted_fields[i], true);
14892 if (type_is_invalid(store_ptr_inst->value->type))
14893 return ira->codegen->invalid_inst_gen;
14894 }
14895
14896 heap::c_allocator.deallocate(field_assign_nodes, actual_field_count);
14897 heap::c_allocator.deallocate(field_values, actual_field_count);
14898 heap::c_allocator.deallocate(casted_fields, actual_field_count);
14899
14900 return ir_get_deref(ira, source_instr, result_loc_inst, nullptr);
14710}14901}
1471114902
14712static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* source_instr,14903static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* source_instr,
...@@ -14727,7 +14918,7 @@ static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* sou...@@ -14727,7 +14918,7 @@ static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* sou
14727 TypeUnionField *union_field = find_union_type_field(union_type, only_field->name);14918 TypeUnionField *union_field = find_union_type_field(union_type, only_field->name);
14728 if (union_field == nullptr) {14919 if (union_field == nullptr) {
14729 ir_add_error_node(ira, only_field->decl_node,14920 ir_add_error_node(ira, only_field->decl_node,
14730 buf_sprintf("no member named '%s' in union '%s'",14921 buf_sprintf("no field named '%s' in union '%s'",
14731 buf_ptr(only_field->name), buf_ptr(&union_type->name)));14922 buf_ptr(only_field->name), buf_ptr(&union_type->name)));
14732 return ira->codegen->invalid_inst_gen;14923 return ira->codegen->invalid_inst_gen;
14733 }14924 }
...@@ -14849,7 +15040,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,...@@ -14849,7 +15040,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
14849 }15040 }
1485015041
14851 // This means the wanted type is anything.15042 // This means the wanted type is anything.
14852 if (wanted_type == ira->codegen->builtin_types.entry_var) {15043 if (wanted_type == ira->codegen->builtin_types.entry_anytype) {
14853 return value;15044 return value;
14854 }15045 }
1485515046
...@@ -15127,46 +15318,6 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,...@@ -15127,46 +15318,6 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
15127 }15318 }
15128 }15319 }
1512915320
15130 // *[N]T to E![]T
15131 if (wanted_type->id == ZigTypeIdErrorUnion &&
15132 is_slice(wanted_type->data.error_union.payload_type) &&
15133 actual_type->id == ZigTypeIdPointer &&
15134 actual_type->data.pointer.ptr_len == PtrLenSingle &&
15135 actual_type->data.pointer.child_type->id == ZigTypeIdArray)
15136 {
15137 ZigType *slice_type = wanted_type->data.error_union.payload_type;
15138 ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry;
15139 assert(slice_ptr_type->id == ZigTypeIdPointer);
15140 ZigType *array_type = actual_type->data.pointer.child_type;
15141 bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0
15142 || !actual_type->data.pointer.is_const);
15143 if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
15144 array_type->data.array.child_type, source_node,
15145 !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
15146 {
15147 // If the pointers both have ABI align, it works.
15148 bool ok_align = slice_ptr_type->data.pointer.explicit_alignment == 0 &&
15149 actual_type->data.pointer.explicit_alignment == 0;
15150 if (!ok_align) {
15151 // If either one has non ABI align, we have to resolve them both
15152 if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type,
15153 ResolveStatusAlignmentKnown)))
15154 {
15155 return ira->codegen->invalid_inst_gen;
15156 }
15157 if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type,
15158 ResolveStatusAlignmentKnown)))
15159 {
15160 return ira->codegen->invalid_inst_gen;
15161 }
15162 ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type);
15163 }
15164 if (ok_align) {
15165 return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, nullptr);
15166 }
15167 }
15168 }
15169
15170 // @Vector(N,T1) to @Vector(N,T2)15321 // @Vector(N,T1) to @Vector(N,T2)
15171 if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector) {15322 if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector) {
15172 if (actual_type->data.vector.len == wanted_type->data.vector.len &&15323 if (actual_type->data.vector.len == wanted_type->data.vector.len &&
...@@ -15346,6 +15497,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,...@@ -15346,6 +15497,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
15346 if (is_pointery_and_elem_is_not_pointery(actual_type)) {15497 if (is_pointery_and_elem_is_not_pointery(actual_type)) {
15347 ZigType *dest_ptr_type = nullptr;15498 ZigType *dest_ptr_type = nullptr;
15348 if (wanted_type->id == ZigTypeIdPointer &&15499 if (wanted_type->id == ZigTypeIdPointer &&
15500 actual_type->id != ZigTypeIdOptional &&
15349 wanted_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void)15501 wanted_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void)
15350 {15502 {
15351 dest_ptr_type = wanted_type;15503 dest_ptr_type = wanted_type;
...@@ -15483,7 +15635,7 @@ static IrInstGen *ir_implicit_cast(IrAnalyze *ira, IrInstGen *value, ZigType *ex...@@ -15483,7 +15635,7 @@ static IrInstGen *ir_implicit_cast(IrAnalyze *ira, IrInstGen *value, ZigType *ex
15483static ZigType *get_ptr_elem_type(CodeGen *g, IrInstGen *ptr) {15635static ZigType *get_ptr_elem_type(CodeGen *g, IrInstGen *ptr) {
15484 ir_assert_gen(ptr->value->type->id == ZigTypeIdPointer, ptr);15636 ir_assert_gen(ptr->value->type->id == ZigTypeIdPointer, ptr);
15485 ZigType *elem_type = ptr->value->type->data.pointer.child_type;15637 ZigType *elem_type = ptr->value->type->data.pointer.child_type;
15486 if (elem_type != g->builtin_types.entry_var)15638 if (elem_type != g->builtin_types.entry_anytype)
15487 return elem_type;15639 return elem_type;
1548815640
15489 if (ir_resolve_lazy(g, ptr->base.source_node, ptr->value))15641 if (ir_resolve_lazy(g, ptr->base.source_node, ptr->value))
...@@ -15535,7 +15687,7 @@ static IrInstGen *ir_get_deref(IrAnalyze *ira, IrInst* source_instruction, IrIns...@@ -15535,7 +15687,7 @@ static IrInstGen *ir_get_deref(IrAnalyze *ira, IrInst* source_instruction, IrIns
15535 }15687 }
15536 if (ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) {15688 if (ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
15537 ZigValue *pointee = const_ptr_pointee_unchecked(ira->codegen, ptr->value);15689 ZigValue *pointee = const_ptr_pointee_unchecked(ira->codegen, ptr->value);
15538 if (child_type == ira->codegen->builtin_types.entry_var) {15690 if (child_type == ira->codegen->builtin_types.entry_anytype) {
15539 child_type = pointee->type;15691 child_type = pointee->type;
15540 }15692 }
15541 if (pointee->special != ConstValSpecialRuntime) {15693 if (pointee->special != ConstValSpecialRuntime) {
...@@ -18353,7 +18505,7 @@ static IrInstGen *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstSrcDeclV...@@ -18353,7 +18505,7 @@ static IrInstGen *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstSrcDeclV
18353 if (decl_var_instruction->var_type != nullptr) {18505 if (decl_var_instruction->var_type != nullptr) {
18354 var_type = decl_var_instruction->var_type->child;18506 var_type = decl_var_instruction->var_type->child;
18355 ZigType *proposed_type = ir_resolve_type(ira, var_type);18507 ZigType *proposed_type = ir_resolve_type(ira, var_type);
18356 explicit_type = validate_var_type(ira->codegen, var_type->base.source_node, proposed_type);18508 explicit_type = validate_var_type(ira->codegen, &var->decl_node->data.variable_declaration, proposed_type);
18357 if (type_is_invalid(explicit_type)) {18509 if (type_is_invalid(explicit_type)) {
18358 var->var_type = ira->codegen->builtin_types.entry_invalid;18510 var->var_type = ira->codegen->builtin_types.entry_invalid;
18359 return ira->codegen->invalid_inst_gen;18511 return ira->codegen->invalid_inst_gen;
...@@ -18935,7 +19087,7 @@ static Error ir_result_has_type(IrAnalyze *ira, ResultLoc *result_loc, bool *out...@@ -18935,7 +19087,7 @@ static Error ir_result_has_type(IrAnalyze *ira, ResultLoc *result_loc, bool *out
18935 ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child);19087 ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child);
18936 if (type_is_invalid(dest_type))19088 if (type_is_invalid(dest_type))
18937 return ErrorSemanticAnalyzeFail;19089 return ErrorSemanticAnalyzeFail;
18938 *out = (dest_type != ira->codegen->builtin_types.entry_var);19090 *out = (dest_type != ira->codegen->builtin_types.entry_anytype);
18939 return ErrorNone;19091 return ErrorNone;
18940 }19092 }
18941 case ResultLocIdVar:19093 case ResultLocIdVar:
...@@ -19141,7 +19293,7 @@ static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_i...@@ -19141,7 +19293,7 @@ static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_i
19141 if (type_is_invalid(dest_type))19293 if (type_is_invalid(dest_type))
19142 return ira->codegen->invalid_inst_gen;19294 return ira->codegen->invalid_inst_gen;
1914319295
19144 if (dest_type == ira->codegen->builtin_types.entry_var) {19296 if (dest_type == ira->codegen->builtin_types.entry_anytype) {
19145 return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type);19297 return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type);
19146 }19298 }
1914719299
...@@ -19287,7 +19439,7 @@ static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_i...@@ -19287,7 +19439,7 @@ static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_i
19287 return ira->codegen->invalid_inst_gen;19439 return ira->codegen->invalid_inst_gen;
19288 }19440 }
1928919441
19290 if (child_type != ira->codegen->builtin_types.entry_var) {19442 if (child_type != ira->codegen->builtin_types.entry_anytype) {
19291 if (type_size(ira->codegen, child_type) != type_size(ira->codegen, value_type)) {19443 if (type_size(ira->codegen, child_type) != type_size(ira->codegen, value_type)) {
19292 // pointer cast won't work; we need a temporary location.19444 // pointer cast won't work; we need a temporary location.
19293 result_bit_cast->parent->written = parent_was_written;19445 result_bit_cast->parent->written = parent_was_written;
...@@ -19448,9 +19600,9 @@ static IrInstGen *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrInstSr...@@ -19448,9 +19600,9 @@ static IrInstGen *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrInstSr
19448 if (type_is_invalid(implicit_elem_type))19600 if (type_is_invalid(implicit_elem_type))
19449 return ira->codegen->invalid_inst_gen;19601 return ira->codegen->invalid_inst_gen;
19450 } else {19602 } else {
19451 implicit_elem_type = ira->codegen->builtin_types.entry_var;19603 implicit_elem_type = ira->codegen->builtin_types.entry_anytype;
19452 }19604 }
19453 if (implicit_elem_type == ira->codegen->builtin_types.entry_var) {19605 if (implicit_elem_type == ira->codegen->builtin_types.entry_anytype) {
19454 Buf *bare_name = buf_alloc();19606 Buf *bare_name = buf_alloc();
19455 Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct),19607 Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct),
19456 instruction->base.base.scope, instruction->base.base.source_node, bare_name);19608 instruction->base.base.scope, instruction->base.base.source_node, bare_name);
...@@ -19607,7 +19759,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node...@@ -19607,7 +19759,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node
19607 assert(param_decl_node->type == NodeTypeParamDecl);19759 assert(param_decl_node->type == NodeTypeParamDecl);
1960819760
19609 IrInstGen *casted_arg;19761 IrInstGen *casted_arg;
19610 if (param_decl_node->data.param_decl.var_token == nullptr) {19762 if (param_decl_node->data.param_decl.anytype_token == nullptr) {
19611 AstNode *param_type_node = param_decl_node->data.param_decl.type;19763 AstNode *param_type_node = param_decl_node->data.param_decl.type;
19612 ZigType *param_type = ir_analyze_type_expr(ira, *exec_scope, param_type_node);19764 ZigType *param_type = ir_analyze_type_expr(ira, *exec_scope, param_type_node);
19613 if (type_is_invalid(param_type))19765 if (type_is_invalid(param_type))
...@@ -19647,7 +19799,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod...@@ -19647,7 +19799,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
19647 arg_part_of_generic_id = true;19799 arg_part_of_generic_id = true;
19648 casted_arg = arg;19800 casted_arg = arg;
19649 } else {19801 } else {
19650 if (param_decl_node->data.param_decl.var_token == nullptr) {19802 if (param_decl_node->data.param_decl.anytype_token == nullptr) {
19651 AstNode *param_type_node = param_decl_node->data.param_decl.type;19803 AstNode *param_type_node = param_decl_node->data.param_decl.type;
19652 ZigType *param_type = ir_analyze_type_expr(ira, *child_scope, param_type_node);19804 ZigType *param_type = ir_analyze_type_expr(ira, *child_scope, param_type_node);
19653 if (type_is_invalid(param_type))19805 if (type_is_invalid(param_type))
...@@ -19859,7 +20011,7 @@ static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr,...@@ -19859,7 +20011,7 @@ static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr,
19859 }20011 }
1986020012
19861 if (ptr->value->type->data.pointer.inferred_struct_field != nullptr &&20013 if (ptr->value->type->data.pointer.inferred_struct_field != nullptr &&
19862 child_type == ira->codegen->builtin_types.entry_var)20014 child_type == ira->codegen->builtin_types.entry_anytype)
19863 {20015 {
19864 child_type = ptr->value->type->data.pointer.inferred_struct_field->inferred_struct_type;20016 child_type = ptr->value->type->data.pointer.inferred_struct_field->inferred_struct_type;
19865 }20017 }
...@@ -20030,7 +20182,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,...@@ -20030,7 +20182,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
20030 }20182 }
2003120183
20032 IrInstGen *first_arg;20184 IrInstGen *first_arg;
20033 if (!first_arg_known_bare && handle_is_ptr(ira->codegen, first_arg_ptr->value->type->data.pointer.child_type)) {20185 if (!first_arg_known_bare) {
20034 first_arg = first_arg_ptr;20186 first_arg = first_arg_ptr;
20035 } else {20187 } else {
20036 first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr);20188 first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr);
...@@ -20050,6 +20202,11 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,...@@ -20050,6 +20202,11 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
20050 }20202 }
2005120203
20052 AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;20204 AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
20205 if (return_type_node == nullptr) {
20206 ir_add_error(ira, &fn_ref->base,
20207 buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
20208 return ira->codegen->invalid_inst_gen;
20209 }
20053 ZigType *specified_return_type = ir_analyze_type_expr(ira, exec_scope, return_type_node);20210 ZigType *specified_return_type = ir_analyze_type_expr(ira, exec_scope, return_type_node);
20054 if (type_is_invalid(specified_return_type))20211 if (type_is_invalid(specified_return_type))
20055 return ira->codegen->invalid_inst_gen;20212 return ira->codegen->invalid_inst_gen;
...@@ -20160,7 +20317,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,...@@ -20160,7 +20317,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
20160 }20317 }
2016120318
20162 IrInstGen *first_arg;20319 IrInstGen *first_arg;
20163 if (!first_arg_known_bare && handle_is_ptr(ira->codegen, first_arg_ptr->value->type->data.pointer.child_type)) {20320 if (!first_arg_known_bare) {
20164 first_arg = first_arg_ptr;20321 first_arg = first_arg_ptr;
20165 } else {20322 } else {
20166 first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr);20323 first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr);
...@@ -20212,7 +20369,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,...@@ -20212,7 +20369,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
20212 inst_fn_type_id.alignment = align_bytes;20369 inst_fn_type_id.alignment = align_bytes;
20213 }20370 }
2021420371
20215 if (fn_proto_node->data.fn_proto.return_var_token == nullptr) {20372 if (fn_proto_node->data.fn_proto.return_anytype_token == nullptr) {
20216 AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;20373 AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
20217 ZigType *specified_return_type = ir_analyze_type_expr(ira, impl_fn->child_scope, return_type_node);20374 ZigType *specified_return_type = ir_analyze_type_expr(ira, impl_fn->child_scope, return_type_node);
20218 if (type_is_invalid(specified_return_type))20375 if (type_is_invalid(specified_return_type))
...@@ -20311,7 +20468,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,...@@ -20311,7 +20468,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
20311 if (type_is_invalid(dummy_result->value->type))20468 if (type_is_invalid(dummy_result->value->type))
20312 return ira->codegen->invalid_inst_gen;20469 return ira->codegen->invalid_inst_gen;
20313 ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;20470 ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;
20314 if (res_child_type == ira->codegen->builtin_types.entry_var) {20471 if (res_child_type == ira->codegen->builtin_types.entry_anytype) {
20315 res_child_type = impl_fn_type_id->return_type;20472 res_child_type = impl_fn_type_id->return_type;
20316 }20473 }
20317 if (!handle_is_ptr(ira->codegen, res_child_type)) {20474 if (!handle_is_ptr(ira->codegen, res_child_type)) {
...@@ -20365,9 +20522,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,...@@ -20365,9 +20522,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
20365 return ira->codegen->invalid_inst_gen;20522 return ira->codegen->invalid_inst_gen;
2036620523
20367 IrInstGen *first_arg;20524 IrInstGen *first_arg;
20368 if (param_type->id == ZigTypeIdPointer &&20525 if (param_type->id == ZigTypeIdPointer) {
20369 handle_is_ptr(ira->codegen, first_arg_ptr->value->type->data.pointer.child_type))
20370 {
20371 first_arg = first_arg_ptr;20526 first_arg = first_arg_ptr;
20372 } else {20527 } else {
20373 first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr);20528 first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr);
...@@ -20454,7 +20609,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,...@@ -20454,7 +20609,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
20454 if (type_is_invalid(dummy_result->value->type))20609 if (type_is_invalid(dummy_result->value->type))
20455 return ira->codegen->invalid_inst_gen;20610 return ira->codegen->invalid_inst_gen;
20456 ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;20611 ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;
20457 if (res_child_type == ira->codegen->builtin_types.entry_var) {20612 if (res_child_type == ira->codegen->builtin_types.entry_anytype) {
20458 res_child_type = return_type;20613 res_child_type = return_type;
20459 }20614 }
20460 if (!handle_is_ptr(ira->codegen, res_child_type)) {20615 if (!handle_is_ptr(ira->codegen, res_child_type)) {
...@@ -20614,40 +20769,106 @@ static IrInstGen *ir_analyze_call_extra(IrAnalyze *ira, IrInst* source_instr,...@@ -20614,40 +20769,106 @@ static IrInstGen *ir_analyze_call_extra(IrAnalyze *ira, IrInst* source_instr,
20614 modifier, stack, stack_src, false, args_ptr, args_len, nullptr, result_loc);20769 modifier, stack, stack_src, false, args_ptr, args_len, nullptr, result_loc);
20615}20770}
2061620771
20617static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCallExtra *instruction) {20772static IrInstGen *ir_analyze_async_call_extra(IrAnalyze *ira, IrInst* source_instr, CallModifier modifier,
20618 IrInstGen *args = instruction->args->child;20773 IrInstSrc *pass1_fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstGen **args_ptr, size_t args_len, ResultLoc *result_loc)
20774{
20775 IrInstGen *fn_ref = pass1_fn_ref->child;
20776 if (type_is_invalid(fn_ref->value->type))
20777 return ira->codegen->invalid_inst_gen;
20778
20779 if (ir_should_inline(ira->old_irb.exec, source_instr->scope)) {
20780 ir_add_error(ira, source_instr, buf_sprintf("TODO: comptime @asyncCall"));
20781 return ira->codegen->invalid_inst_gen;
20782 }
20783
20784 IrInstGen *first_arg_ptr = nullptr;
20785 IrInst *first_arg_ptr_src = nullptr;
20786 ZigFn *fn = nullptr;
20787 if (instr_is_comptime(fn_ref)) {
20788 if (fn_ref->value->type->id == ZigTypeIdBoundFn) {
20789 assert(fn_ref->value->special == ConstValSpecialStatic);
20790 fn = fn_ref->value->data.x_bound_fn.fn;
20791 first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg;
20792 first_arg_ptr_src = fn_ref->value->data.x_bound_fn.first_arg_src;
20793 if (type_is_invalid(first_arg_ptr->value->type))
20794 return ira->codegen->invalid_inst_gen;
20795 } else {
20796 fn = ir_resolve_fn(ira, fn_ref);
20797 }
20798 }
20799
20800 IrInstGen *ret_ptr_uncasted = nullptr;
20801 if (ret_ptr != nullptr) {
20802 ret_ptr_uncasted = ret_ptr->child;
20803 if (type_is_invalid(ret_ptr_uncasted->value->type))
20804 return ira->codegen->invalid_inst_gen;
20805 }
20806
20807 ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type;
20808 IrInstGen *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack->child,
20809 &new_stack->base, true, fn);
20810 if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type))
20811 return ira->codegen->invalid_inst_gen;
20812
20813 return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, first_arg_ptr_src,
20814 modifier, casted_new_stack, &new_stack->base, true, args_ptr, args_len, ret_ptr_uncasted, result_loc);
20815}
20816
20817static bool ir_extract_tuple_call_args(IrAnalyze *ira, IrInst *source_instr, IrInstGen *args, IrInstGen ***args_ptr, size_t *args_len) {
20619 ZigType *args_type = args->value->type;20818 ZigType *args_type = args->value->type;
20620 if (type_is_invalid(args_type))20819 if (type_is_invalid(args_type))
20621 return ira->codegen->invalid_inst_gen;20820 return false;
2062220821
20623 if (args_type->id != ZigTypeIdStruct) {20822 if (args_type->id != ZigTypeIdStruct) {
20624 ir_add_error(ira, &args->base,20823 ir_add_error(ira, &args->base,
20625 buf_sprintf("expected tuple or struct, found '%s'", buf_ptr(&args_type->name)));20824 buf_sprintf("expected tuple or struct, found '%s'", buf_ptr(&args_type->name)));
20626 return ira->codegen->invalid_inst_gen;20825 return false;
20627 }20826 }
2062820827
20629 IrInstGen **args_ptr = nullptr;
20630 size_t args_len = 0;
20631
20632 if (is_tuple(args_type)) {20828 if (is_tuple(args_type)) {
20633 args_len = args_type->data.structure.src_field_count;20829 *args_len = args_type->data.structure.src_field_count;
20634 args_ptr = heap::c_allocator.allocate<IrInstGen *>(args_len);20830 *args_ptr = heap::c_allocator.allocate<IrInstGen *>(*args_len);
20635 for (size_t i = 0; i < args_len; i += 1) {20831 for (size_t i = 0; i < *args_len; i += 1) {
20636 TypeStructField *arg_field = args_type->data.structure.fields[i];20832 TypeStructField *arg_field = args_type->data.structure.fields[i];
20637 args_ptr[i] = ir_analyze_struct_value_field_value(ira, &instruction->base.base, args, arg_field);20833 (*args_ptr)[i] = ir_analyze_struct_value_field_value(ira, source_instr, args, arg_field);
20638 if (type_is_invalid(args_ptr[i]->value->type))20834 if (type_is_invalid((*args_ptr)[i]->value->type))
20639 return ira->codegen->invalid_inst_gen;20835 return false;
20640 }20836 }
20641 } else {20837 } else {
20642 ir_add_error(ira, &args->base, buf_sprintf("TODO: struct args"));20838 ir_add_error(ira, &args->base, buf_sprintf("TODO: struct args"));
20839 return false;
20840 }
20841 return true;
20842}
20843
20844static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCallExtra *instruction) {
20845 IrInstGen *args = instruction->args->child;
20846 IrInstGen **args_ptr = nullptr;
20847 size_t args_len = 0;
20848 if (!ir_extract_tuple_call_args(ira, &instruction->base.base, args, &args_ptr, &args_len)) {
20643 return ira->codegen->invalid_inst_gen;20849 return ira->codegen->invalid_inst_gen;
20644 }20850 }
20851
20645 IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options,20852 IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options,
20646 instruction->fn_ref, args_ptr, args_len, instruction->result_loc);20853 instruction->fn_ref, args_ptr, args_len, instruction->result_loc);
20647 heap::c_allocator.deallocate(args_ptr, args_len);20854 heap::c_allocator.deallocate(args_ptr, args_len);
20648 return result;20855 return result;
20649}20856}
2065020857
20858static IrInstGen *ir_analyze_instruction_async_call_extra(IrAnalyze *ira, IrInstSrcAsyncCallExtra *instruction) {
20859 IrInstGen *args = instruction->args->child;
20860 IrInstGen **args_ptr = nullptr;
20861 size_t args_len = 0;
20862 if (!ir_extract_tuple_call_args(ira, &instruction->base.base, args, &args_ptr, &args_len)) {
20863 return ira->codegen->invalid_inst_gen;
20864 }
20865
20866 IrInstGen *result = ir_analyze_async_call_extra(ira, &instruction->base.base, instruction->modifier,
20867 instruction->fn_ref, instruction->ret_ptr, instruction->new_stack, args_ptr, args_len, instruction->result_loc);
20868 heap::c_allocator.deallocate(args_ptr, args_len);
20869 return result;
20870}
20871
20651static IrInstGen *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstSrcCallArgs *instruction) {20872static IrInstGen *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstSrcCallArgs *instruction) {
20652 IrInstGen **args_ptr = heap::c_allocator.allocate<IrInstGen *>(instruction->args_len);20873 IrInstGen **args_ptr = heap::c_allocator.allocate<IrInstGen *>(instruction->args_len);
20653 for (size_t i = 0; i < instruction->args_len; i += 1) {20874 for (size_t i = 0; i < instruction->args_len; i += 1) {
...@@ -20881,17 +21102,24 @@ static IrInstGen *ir_analyze_negation(IrAnalyze *ira, IrInstSrcUnOp *instruction...@@ -20881,17 +21102,24 @@ static IrInstGen *ir_analyze_negation(IrAnalyze *ira, IrInstSrcUnOp *instruction
20881 if (type_is_invalid(expr_type))21102 if (type_is_invalid(expr_type))
20882 return ira->codegen->invalid_inst_gen;21103 return ira->codegen->invalid_inst_gen;
2088321104
20884 if (!(expr_type->id == ZigTypeIdInt || expr_type->id == ZigTypeIdComptimeInt ||
20885 expr_type->id == ZigTypeIdFloat || expr_type->id == ZigTypeIdComptimeFloat ||
20886 expr_type->id == ZigTypeIdVector))
20887 {
20888 ir_add_error(ira, &instruction->base.base,
20889 buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name)));
20890 return ira->codegen->invalid_inst_gen;
20891 }
20892
20893 bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap);21105 bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap);
2089421106
21107 switch (expr_type->id) {
21108 case ZigTypeIdComptimeInt:
21109 case ZigTypeIdFloat:
21110 case ZigTypeIdComptimeFloat:
21111 case ZigTypeIdVector:
21112 break;
21113 case ZigTypeIdInt:
21114 if (is_wrap_op || expr_type->data.integral.is_signed)
21115 break;
21116 ZIG_FALLTHROUGH;
21117 default:
21118 ir_add_error(ira, &instruction->base.base,
21119 buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name)));
21120 return ira->codegen->invalid_inst_gen;
21121 }
21122
20895 ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;21123 ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
2089621124
20897 if (instr_is_comptime(value)) {21125 if (instr_is_comptime(value)) {
...@@ -22112,7 +22340,7 @@ static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name,...@@ -22112,7 +22340,7 @@ static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name,
22112 inferred_struct_field->inferred_struct_type = container_type;22340 inferred_struct_field->inferred_struct_type = container_type;
22113 inferred_struct_field->field_name = field_name;22341 inferred_struct_field->field_name = field_name;
2211422342
22115 ZigType *elem_type = ira->codegen->builtin_types.entry_var;22343 ZigType *elem_type = ira->codegen->builtin_types.entry_anytype;
22116 ZigType *field_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type,22344 ZigType *field_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
22117 container_ptr_type->data.pointer.is_const, container_ptr_type->data.pointer.is_volatile,22345 container_ptr_type->data.pointer.is_const, container_ptr_type->data.pointer.is_volatile,
22118 PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field, nullptr);22346 PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field, nullptr);
...@@ -22407,7 +22635,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel...@@ -22407,7 +22635,7 @@ static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFiel
22407 usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);22635 usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
22408 } else {22636 } else {
22409 ir_add_error_node(ira, source_node,22637 ir_add_error_node(ira, source_node,
22410 buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),22638 buf_sprintf("no field named '%s' in '%s'", buf_ptr(field_name),
22411 buf_ptr(&container_type->name)));22639 buf_ptr(&container_type->name)));
22412 return ira->codegen->invalid_inst_gen;22640 return ira->codegen->invalid_inst_gen;
22413 }22641 }
...@@ -23834,7 +24062,7 @@ static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instructi...@@ -23834,7 +24062,7 @@ static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instructi
23834 TypeUnionField *type_field = find_union_type_field(union_type, field_name);24062 TypeUnionField *type_field = find_union_type_field(union_type, field_name);
23835 if (type_field == nullptr) {24063 if (type_field == nullptr) {
23836 ir_add_error_node(ira, field_source_node,24064 ir_add_error_node(ira, field_source_node,
23837 buf_sprintf("no member named '%s' in union '%s'",24065 buf_sprintf("no field named '%s' in union '%s'",
23838 buf_ptr(field_name), buf_ptr(&union_type->name)));24066 buf_ptr(field_name), buf_ptr(&union_type->name)));
23839 return ira->codegen->invalid_inst_gen;24067 return ira->codegen->invalid_inst_gen;
23840 }24068 }
...@@ -23930,7 +24158,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc...@@ -23930,7 +24158,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc
23930 TypeStructField *type_field = find_struct_type_field(container_type, field->name);24158 TypeStructField *type_field = find_struct_type_field(container_type, field->name);
23931 if (!type_field) {24159 if (!type_field) {
23932 ir_add_error_node(ira, field->source_node,24160 ir_add_error_node(ira, field->source_node,
23933 buf_sprintf("no member named '%s' in struct '%s'",24161 buf_sprintf("no field named '%s' in struct '%s'",
23934 buf_ptr(field->name), buf_ptr(&container_type->name)));24162 buf_ptr(field->name), buf_ptr(&container_type->name)));
23935 return ira->codegen->invalid_inst_gen;24163 return ira->codegen->invalid_inst_gen;
23936 }24164 }
...@@ -23965,7 +24193,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc...@@ -23965,7 +24193,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc
23965 memoize_field_init_val(ira->codegen, container_type, field);24193 memoize_field_init_val(ira->codegen, container_type, field);
23966 if (field->init_val == nullptr) {24194 if (field->init_val == nullptr) {
23967 ir_add_error(ira, source_instr,24195 ir_add_error(ira, source_instr,
23968 buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i]->name)));24196 buf_sprintf("missing field: '%s'", buf_ptr(field->name)));
23969 any_missing = true;24197 any_missing = true;
23970 continue;24198 continue;
23971 }24199 }
...@@ -24890,7 +25118,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent...@@ -24890,7 +25118,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
24890 fields[5]->special = ConstValSpecialStatic;25118 fields[5]->special = ConstValSpecialStatic;
24891 fields[5]->type = ira->codegen->builtin_types.entry_bool;25119 fields[5]->type = ira->codegen->builtin_types.entry_bool;
24892 fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;25120 fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;
24893 // sentinel: var25121 // sentinel: anytype
24894 ensure_field_index(result->type, "sentinel", 6);25122 ensure_field_index(result->type, "sentinel", 6);
24895 fields[6]->special = ConstValSpecialStatic;25123 fields[6]->special = ConstValSpecialStatic;
24896 if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) {25124 if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) {
...@@ -25018,7 +25246,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy...@@ -25018,7 +25246,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25018 fields[1]->special = ConstValSpecialStatic;25246 fields[1]->special = ConstValSpecialStatic;
25019 fields[1]->type = ira->codegen->builtin_types.entry_type;25247 fields[1]->type = ira->codegen->builtin_types.entry_type;
25020 fields[1]->data.x_type = type_entry->data.array.child_type;25248 fields[1]->data.x_type = type_entry->data.array.child_type;
25021 // sentinel: var25249 // sentinel: anytype
25022 fields[2]->special = ConstValSpecialStatic;25250 fields[2]->special = ConstValSpecialStatic;
25023 fields[2]->type = get_optional_type(ira->codegen, type_entry->data.array.child_type);25251 fields[2]->type = get_optional_type(ira->codegen, type_entry->data.array.child_type);
25024 fields[2]->data.x_optional = type_entry->data.array.sentinel;25252 fields[2]->data.x_optional = type_entry->data.array.sentinel;
...@@ -25318,7 +25546,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy...@@ -25318,7 +25546,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25318 result->special = ConstValSpecialStatic;25546 result->special = ConstValSpecialStatic;
25319 result->type = ir_type_info_get_type(ira, "Struct", nullptr);25547 result->type = ir_type_info_get_type(ira, "Struct", nullptr);
2532025548
25321 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 3);25549 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4);
25322 result->data.x_struct.fields = fields;25550 result->data.x_struct.fields = fields;
2532325551
25324 // layout: ContainerLayout25552 // layout: ContainerLayout
...@@ -25373,7 +25601,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy...@@ -25373,7 +25601,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25373 inner_fields[2]->type = ira->codegen->builtin_types.entry_type;25601 inner_fields[2]->type = ira->codegen->builtin_types.entry_type;
25374 inner_fields[2]->data.x_type = struct_field->type_entry;25602 inner_fields[2]->data.x_type = struct_field->type_entry;
2537525603
25376 // default_value: var25604 // default_value: anytype
25377 inner_fields[3]->special = ConstValSpecialStatic;25605 inner_fields[3]->special = ConstValSpecialStatic;
25378 inner_fields[3]->type = get_optional_type2(ira->codegen, struct_field->type_entry);25606 inner_fields[3]->type = get_optional_type2(ira->codegen, struct_field->type_entry);
25379 if (inner_fields[3]->type == nullptr) return ErrorSemanticAnalyzeFail;25607 if (inner_fields[3]->type == nullptr) return ErrorSemanticAnalyzeFail;
...@@ -25399,6 +25627,12 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy...@@ -25399,6 +25627,12 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25399 return err;25627 return err;
25400 }25628 }
2540125629
25630 // is_tuple: bool
25631 ensure_field_index(result->type, "is_tuple", 3);
25632 fields[3]->special = ConstValSpecialStatic;
25633 fields[3]->type = ira->codegen->builtin_types.entry_bool;
25634 fields[3]->data.x_bool = is_tuple(type_entry);
25635
25402 break;25636 break;
25403 }25637 }
25404 case ZigTypeIdFn:25638 case ZigTypeIdFn:
...@@ -25504,9 +25738,18 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy...@@ -25504,9 +25738,18 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25504 break;25738 break;
25505 }25739 }
25506 case ZigTypeIdFnFrame:25740 case ZigTypeIdFnFrame:
25507 ir_add_error(ira, source_instr,25741 {
25508 buf_sprintf("compiler bug: TODO @typeInfo for async function frames. https://github.com/ziglang/zig/issues/3066"));25742 result = ira->codegen->pass1_arena->create<ZigValue>();
25509 return ErrorSemanticAnalyzeFail;25743 result->special = ConstValSpecialStatic;
25744 result->type = ir_type_info_get_type(ira, "Frame", nullptr);
25745 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1);
25746 result->data.x_struct.fields = fields;
25747 ZigFn *fn = type_entry->data.frame.fn;
25748 // function: anytype
25749 ensure_field_index(result->type, "function", 0);
25750 fields[0] = create_const_fn(ira->codegen, fn);
25751 break;
25752 }
25510 }25753 }
2551125754
25512 assert(result != nullptr);25755 assert(result != nullptr);
...@@ -25676,6 +25919,11 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -25676,6 +25919,11 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
25676 {25919 {
25677 return ira->codegen->invalid_inst_gen->value->type;25920 return ira->codegen->invalid_inst_gen->value->type;
25678 }25921 }
25922 if (sentinel != nullptr && (size_enum_index == BuiltinPtrSizeOne || size_enum_index == BuiltinPtrSizeC)) {
25923 ir_add_error(ira, source_instr,
25924 buf_sprintf("sentinels are only allowed on slices and unknown-length pointers"));
25925 return ira->codegen->invalid_inst_gen->value->type;
25926 }
25679 BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 3);25927 BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 3);
25680 if (bi == nullptr)25928 if (bi == nullptr)
25681 return ira->codegen->invalid_inst_gen->value->type;25929 return ira->codegen->invalid_inst_gen->value->type;
...@@ -25709,7 +25957,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -25709,7 +25957,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
25709 0, // host_int_bytes25957 0, // host_int_bytes
25710 is_allowzero,25958 is_allowzero,
25711 VECTOR_INDEX_NONE, nullptr, sentinel);25959 VECTOR_INDEX_NONE, nullptr, sentinel);
25712 if (size_enum_index != 2)25960 if (size_enum_index != BuiltinPtrSizeSlice)
25713 return ptr_type;25961 return ptr_type;
25714 return get_slice_type(ira->codegen, ptr_type);25962 return get_slice_type(ira->codegen, ptr_type);
25715 }25963 }
...@@ -25775,10 +26023,90 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI...@@ -25775,10 +26023,90 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
25775 ZigType *child_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "child", 0);26023 ZigType *child_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "child", 0);
25776 return get_any_frame_type(ira->codegen, child_type);26024 return get_any_frame_type(ira->codegen, child_type);
25777 }26025 }
25778 case ZigTypeIdErrorSet:
25779 case ZigTypeIdEnum:
25780 case ZigTypeIdFnFrame:
25781 case ZigTypeIdEnumLiteral:26026 case ZigTypeIdEnumLiteral:
26027 return ira->codegen->builtin_types.entry_enum_literal;
26028 case ZigTypeIdFnFrame: {
26029 assert(payload->special == ConstValSpecialStatic);
26030 assert(payload->type == ir_type_info_get_type(ira, "Frame", nullptr));
26031 ZigValue *function = get_const_field(ira, source_instr->source_node, payload, "function", 0);
26032 assert(function->type->id == ZigTypeIdFn);
26033 ZigFn *fn = function->data.x_ptr.data.fn.fn_entry;
26034 return get_fn_frame_type(ira->codegen, fn);
26035 }
26036 case ZigTypeIdErrorSet: {
26037 assert(payload->special == ConstValSpecialStatic);
26038 assert(payload->type->id == ZigTypeIdOptional);
26039 ZigValue *slice = payload->data.x_optional;
26040 if (slice == nullptr)
26041 return ira->codegen->builtin_types.entry_global_error_set;
26042 assert(slice->special == ConstValSpecialStatic);
26043 assert(is_slice(slice->type));
26044 ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
26045 Buf bare_name = BUF_INIT;
26046 buf_init_from_buf(&err_set_type->name, get_anon_type_name(ira->codegen, ira->old_irb.exec, "error", source_instr->scope, source_instr->source_node, &bare_name));
26047 err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits;
26048 err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align;
26049 err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size;
26050 ZigValue *ptr = slice->data.x_struct.fields[slice_ptr_index];
26051 assert(ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);;
26052 assert(ptr->data.x_ptr.data.base_array.elem_index == 0);
26053 ZigValue *arr = ptr->data.x_ptr.data.base_array.array_val;
26054 assert(arr->special == ConstValSpecialStatic);
26055 assert(arr->data.x_array.special == ConstArraySpecialNone);
26056 ZigValue *len = slice->data.x_struct.fields[slice_len_index];
26057 size_t count = bigint_as_usize(&len->data.x_bigint);
26058 err_set_type->data.error_set.err_count = count;
26059 err_set_type->data.error_set.errors = heap::c_allocator.allocate<ErrorTableEntry *>(count);
26060 bool *already_set = heap::c_allocator.allocate<bool>(ira->codegen->errors_by_index.length + count);
26061 for (size_t i = 0; i < count; i++) {
26062 ZigValue *error = &arr->data.x_array.data.s_none.elements[i];
26063 assert(error->type == ir_type_info_get_type(ira, "Error", nullptr));
26064 ErrorTableEntry *err_entry = heap::c_allocator.create<ErrorTableEntry>();
26065 err_entry->decl_node = source_instr->source_node;
26066 ZigValue *name_slice = get_const_field(ira, source_instr->source_node, error, "name", 0);
26067 ZigValue *name_ptr = name_slice->data.x_struct.fields[slice_ptr_index];
26068 ZigValue *name_len = name_slice->data.x_struct.fields[slice_len_index];
26069 assert(name_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);
26070 assert(name_ptr->data.x_ptr.data.base_array.elem_index == 0);
26071 ZigValue *name_arr = name_ptr->data.x_ptr.data.base_array.array_val;
26072 assert(name_arr->special == ConstValSpecialStatic);
26073 switch (name_arr->data.x_array.special) {
26074 case ConstArraySpecialUndef:
26075 return ira->codegen->invalid_inst_gen->value->type;
26076 case ConstArraySpecialNone: {
26077 buf_resize(&err_entry->name, 0);
26078 size_t name_count = bigint_as_usize(&name_len->data.x_bigint);
26079 for (size_t j = 0; j < name_count; j++) {
26080 ZigValue *ch_val = &name_arr->data.x_array.data.s_none.elements[j];
26081 unsigned ch = bigint_as_u32(&ch_val->data.x_bigint);
26082 buf_append_char(&err_entry->name, ch);
26083 }
26084 break;
26085 }
26086 case ConstArraySpecialBuf:
26087 buf_init_from_buf(&err_entry->name, name_arr->data.x_array.data.s_buf);
26088 break;
26089 }
26090 auto existing_entry = ira->codegen->error_table.put_unique(&err_entry->name, err_entry);
26091 if (existing_entry) {
26092 err_entry->value = existing_entry->value->value;
26093 } else {
26094 size_t error_value_count = ira->codegen->errors_by_index.length;
26095 assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count));
26096 err_entry->value = error_value_count;
26097 ira->codegen->errors_by_index.append(err_entry);
26098 }
26099 if (already_set[err_entry->value]) {
26100 ir_add_error(ira, source_instr, buf_sprintf("duplicate error: %s", buf_ptr(&err_entry->name)));
26101 return ira->codegen->invalid_inst_gen->value->type;
26102 } else {
26103 already_set[err_entry->value] = true;
26104 }
26105 err_set_type->data.error_set.errors[i] = err_entry;
26106 }
26107 return err_set_type;
26108 }
26109 case ZigTypeIdEnum:
25782 ir_add_error(ira, source_instr, buf_sprintf(26110 ir_add_error(ira, source_instr, buf_sprintf(
25783 "TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId)));26111 "TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId)));
25784 return ira->codegen->invalid_inst_gen->value->type;26112 return ira->codegen->invalid_inst_gen->value->type;
...@@ -26370,6 +26698,10 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa...@@ -26370,6 +26698,10 @@ static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCa
26370 }26698 }
2637126699
26372 if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeInt) {26700 if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeInt) {
26701 ZigValue *val = ir_resolve_const(ira, target, UndefBad);
26702 if (val == nullptr)
26703 return ira->codegen->invalid_inst_gen;
26704
26373 return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type);26705 return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type);
26374 }26706 }
2637526707
...@@ -26407,16 +26739,20 @@ static IrInstGen *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstSrcFlo...@@ -26407,16 +26739,20 @@ static IrInstGen *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstSrcFlo
26407 }26739 }
26408 }26740 }
2640926741
26410 if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeFloat) {
26411 return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type);
26412 }
26413
26414 if (target->value->type->id != ZigTypeIdFloat) {26742 if (target->value->type->id != ZigTypeIdFloat) {
26415 ir_add_error(ira, &instruction->target->base, buf_sprintf("expected float type, found '%s'",26743 ir_add_error(ira, &instruction->target->base, buf_sprintf("expected float type, found '%s'",
26416 buf_ptr(&target->value->type->name)));26744 buf_ptr(&target->value->type->name)));
26417 return ira->codegen->invalid_inst_gen;26745 return ira->codegen->invalid_inst_gen;
26418 }26746 }
2641926747
26748 if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeFloat) {
26749 ZigValue *val = ir_resolve_const(ira, target, UndefBad);
26750 if (val == nullptr)
26751 return ira->codegen->invalid_inst_gen;
26752
26753 return ir_analyze_widen_or_shorten(ira, &instruction->target->base, target, dest_type);
26754 }
26755
26420 return ir_analyze_widen_or_shorten(ira, &instruction->base.base, target, dest_type);26756 return ir_analyze_widen_or_shorten(ira, &instruction->base.base, target, dest_type);
26421}26757}
2642226758
...@@ -28660,7 +28996,37 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -28660,7 +28996,37 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
28660 ir_add_error(ira, &instruction->base.base,28996 ir_add_error(ira, &instruction->base.base,
28661 buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));28997 buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
28662 return ira->codegen->invalid_inst_gen;28998 return ira->codegen->invalid_inst_gen;
28663 }28999 } else if(switch_type->id == ZigTypeIdMetaType) {
29000 HashMap<const ZigType*, IrInstGen*, type_ptr_hash, type_ptr_eql> prevs;
29001 // HashMap doubles capacity when reaching 60% capacity,
29002 // because we know the size at init we can avoid reallocation by doubling it here
29003 prevs.init(instruction->range_count * 2);
29004 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
29005 IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i];
29006
29007 IrInstGen *value = range->start->child;
29008 IrInstGen *casted_value = ir_implicit_cast(ira, value, switch_type);
29009 if (type_is_invalid(casted_value->value->type)) {
29010 prevs.deinit();
29011 return ira->codegen->invalid_inst_gen;
29012 }
29013
29014 ZigValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad);
29015 if (!const_expr_val) {
29016 prevs.deinit();
29017 return ira->codegen->invalid_inst_gen;
29018 }
29019
29020 auto entry = prevs.put_unique(const_expr_val->data.x_type, value);
29021 if(entry != nullptr) {
29022 ErrorMsg *msg = ir_add_error(ira, &value->base, buf_sprintf("duplicate switch value"));
29023 add_error_note(ira->codegen, msg, entry->value->base.source_node, buf_sprintf("previous value is here"));
29024 prevs.deinit();
29025 return ira->codegen->invalid_inst_gen;
29026 }
29027 }
29028 prevs.deinit();
29029 }
28664 return ir_const_void(ira, &instruction->base.base);29030 return ir_const_void(ira, &instruction->base.base);
28665}29031}
2866629032
...@@ -29650,7 +30016,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy...@@ -29650,7 +30016,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
29650 if (arg_index >= fn_type_id->param_count) {30016 if (arg_index >= fn_type_id->param_count) {
29651 if (instruction->allow_var) {30017 if (instruction->allow_var) {
29652 // TODO remove this with var args30018 // TODO remove this with var args
29653 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_var);30019 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
29654 }30020 }
29655 ir_add_error(ira, &arg_index_inst->base,30021 ir_add_error(ira, &arg_index_inst->base,
29656 buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments",30022 buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments",
...@@ -29664,7 +30030,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy...@@ -29664,7 +30030,7 @@ static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgTy
29664 ir_assert(fn_type->data.fn.is_generic, &instruction->base.base);30030 ir_assert(fn_type->data.fn.is_generic, &instruction->base.base);
2966530031
29666 if (instruction->allow_var) {30032 if (instruction->allow_var) {
29667 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_var);30033 return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype);
29668 } else {30034 } else {
29669 ir_add_error(ira, &arg_index_inst->base,30035 ir_add_error(ira, &arg_index_inst->base,
29670 buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic",30036 buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic",
...@@ -30173,6 +30539,21 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinF...@@ -30173,6 +30539,21 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinF
30173 case BuiltinFnIdSqrt:30539 case BuiltinFnIdSqrt:
30174 f128M_sqrt(in, out);30540 f128M_sqrt(in, out);
30175 break;30541 break;
30542 case BuiltinFnIdFabs:
30543 f128M_abs(in, out);
30544 break;
30545 case BuiltinFnIdFloor:
30546 f128M_roundToInt(in, softfloat_round_min, false, out);
30547 break;
30548 case BuiltinFnIdCeil:
30549 f128M_roundToInt(in, softfloat_round_max, false, out);
30550 break;
30551 case BuiltinFnIdTrunc:
30552 f128M_trunc(in, out);
30553 break;
30554 case BuiltinFnIdRound:
30555 f128M_roundToInt(in, softfloat_round_near_maxMag, false, out);
30556 break;
30176 case BuiltinFnIdNearbyInt:30557 case BuiltinFnIdNearbyInt:
30177 case BuiltinFnIdSin:30558 case BuiltinFnIdSin:
30178 case BuiltinFnIdCos:30559 case BuiltinFnIdCos:
...@@ -30181,11 +30562,6 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinF...@@ -30181,11 +30562,6 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinF
30181 case BuiltinFnIdLog:30562 case BuiltinFnIdLog:
30182 case BuiltinFnIdLog10:30563 case BuiltinFnIdLog10:
30183 case BuiltinFnIdLog2:30564 case BuiltinFnIdLog2:
30184 case BuiltinFnIdFabs:
30185 case BuiltinFnIdFloor:
30186 case BuiltinFnIdCeil:
30187 case BuiltinFnIdTrunc:
30188 case BuiltinFnIdRound:
30189 return ir_add_error(ira, source_instr,30565 return ir_add_error(ira, source_instr,
30190 buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026",30566 buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026",
30191 float_op_to_name(fop), buf_ptr(&float_type->name)));30567 float_op_to_name(fop), buf_ptr(&float_type->name)));
...@@ -30769,6 +31145,64 @@ static IrInstGen *ir_analyze_instruction_spill_end(IrAnalyze *ira, IrInstSrcSpil...@@ -30769,6 +31145,64 @@ static IrInstGen *ir_analyze_instruction_spill_end(IrAnalyze *ira, IrInstSrcSpil
30769 return ir_build_spill_end_gen(ira, &instruction->base.base, begin, operand->value->type);31145 return ir_build_spill_end_gen(ira, &instruction->base.base, begin, operand->value->type);
30770}31146}
3077131147
31148static IrInstGen *ir_analyze_instruction_src(IrAnalyze *ira, IrInstSrcSrc *instruction) {
31149 ZigFn *fn_entry = scope_fn_entry(instruction->base.base.scope);
31150 if (fn_entry == nullptr) {
31151 ir_add_error(ira, &instruction->base.base, buf_sprintf("@src outside function"));
31152 return ira->codegen->invalid_inst_gen;
31153 }
31154
31155 ZigType *u8_ptr = get_pointer_to_type_extra2(
31156 ira->codegen, ira->codegen->builtin_types.entry_u8,
31157 true, false, PtrLenUnknown,
31158 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, ira->codegen->intern.for_zero_byte());
31159 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
31160
31161 ZigType *source_location_type = get_builtin_type(ira->codegen, "SourceLocation");
31162 if (type_resolve(ira->codegen, source_location_type, ResolveStatusSizeKnown)) {
31163 zig_unreachable();
31164 }
31165
31166 ZigValue *result = ira->codegen->pass1_arena->create<ZigValue>();
31167 result->special = ConstValSpecialStatic;
31168 result->type = source_location_type;
31169
31170 ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4);
31171 result->data.x_struct.fields = fields;
31172
31173 // file: [:0]const u8
31174 ensure_field_index(source_location_type, "file", 0);
31175 fields[0]->special = ConstValSpecialStatic;
31176
31177 ZigType *import = instruction->base.base.source_node->owner;
31178 Buf *path = import->data.structure.root_struct->path;
31179 ZigValue *file_name = create_const_str_lit(ira->codegen, path)->data.x_ptr.data.ref.pointee;
31180 init_const_slice(ira->codegen, fields[0], file_name, 0, buf_len(path), true);
31181 fields[0]->type = u8_slice;
31182
31183 // fn_name: [:0]const u8
31184 ensure_field_index(source_location_type, "fn_name", 1);
31185 fields[1]->special = ConstValSpecialStatic;
31186
31187 ZigValue *fn_name = create_const_str_lit(ira->codegen, &fn_entry->symbol_name)->data.x_ptr.data.ref.pointee;
31188 init_const_slice(ira->codegen, fields[1], fn_name, 0, buf_len(&fn_entry->symbol_name), true);
31189 fields[1]->type = u8_slice;
31190
31191 // line: u32
31192 ensure_field_index(source_location_type, "line", 2);
31193 fields[2]->special = ConstValSpecialStatic;
31194 fields[2]->type = ira->codegen->builtin_types.entry_u32;
31195 bigint_init_unsigned(&fields[2]->data.x_bigint, instruction->base.base.source_node->line + 1);
31196
31197 // column: u32
31198 ensure_field_index(source_location_type, "column", 3);
31199 fields[3]->special = ConstValSpecialStatic;
31200 fields[3]->type = ira->codegen->builtin_types.entry_u32;
31201 bigint_init_unsigned(&fields[3]->data.x_bigint, instruction->base.base.source_node->column + 1);
31202
31203 return ir_const_move(ira, &instruction->base.base, result);
31204}
31205
30772static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruction) {31206static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruction) {
30773 switch (instruction->id) {31207 switch (instruction->id) {
30774 case IrInstSrcIdInvalid:31208 case IrInstSrcIdInvalid:
...@@ -30802,6 +31236,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc...@@ -30802,6 +31236,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
30802 return ir_analyze_instruction_call_args(ira, (IrInstSrcCallArgs *)instruction);31236 return ir_analyze_instruction_call_args(ira, (IrInstSrcCallArgs *)instruction);
30803 case IrInstSrcIdCallExtra:31237 case IrInstSrcIdCallExtra:
30804 return ir_analyze_instruction_call_extra(ira, (IrInstSrcCallExtra *)instruction);31238 return ir_analyze_instruction_call_extra(ira, (IrInstSrcCallExtra *)instruction);
31239 case IrInstSrcIdAsyncCallExtra:
31240 return ir_analyze_instruction_async_call_extra(ira, (IrInstSrcAsyncCallExtra *)instruction);
30805 case IrInstSrcIdBr:31241 case IrInstSrcIdBr:
30806 return ir_analyze_instruction_br(ira, (IrInstSrcBr *)instruction);31242 return ir_analyze_instruction_br(ira, (IrInstSrcBr *)instruction);
30807 case IrInstSrcIdCondBr:31243 case IrInstSrcIdCondBr:
...@@ -31040,6 +31476,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc...@@ -31040,6 +31476,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
31040 return ir_analyze_instruction_wasm_memory_size(ira, (IrInstSrcWasmMemorySize *)instruction);31476 return ir_analyze_instruction_wasm_memory_size(ira, (IrInstSrcWasmMemorySize *)instruction);
31041 case IrInstSrcIdWasmMemoryGrow:31477 case IrInstSrcIdWasmMemoryGrow:
31042 return ir_analyze_instruction_wasm_memory_grow(ira, (IrInstSrcWasmMemoryGrow *)instruction);31478 return ir_analyze_instruction_wasm_memory_grow(ira, (IrInstSrcWasmMemoryGrow *)instruction);
31479 case IrInstSrcIdSrc:
31480 return ir_analyze_instruction_src(ira, (IrInstSrcSrc *)instruction);
31043 }31481 }
31044 zig_unreachable();31482 zig_unreachable();
31045}31483}
...@@ -31309,6 +31747,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {...@@ -31309,6 +31747,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
31309 case IrInstSrcIdDeclVar:31747 case IrInstSrcIdDeclVar:
31310 case IrInstSrcIdStorePtr:31748 case IrInstSrcIdStorePtr:
31311 case IrInstSrcIdCallExtra:31749 case IrInstSrcIdCallExtra:
31750 case IrInstSrcIdAsyncCallExtra:
31312 case IrInstSrcIdCall:31751 case IrInstSrcIdCall:
31313 case IrInstSrcIdCallArgs:31752 case IrInstSrcIdCallArgs:
31314 case IrInstSrcIdReturn:31753 case IrInstSrcIdReturn:
...@@ -31435,6 +31874,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {...@@ -31435,6 +31874,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
31435 case IrInstSrcIdAlloca:31874 case IrInstSrcIdAlloca:
31436 case IrInstSrcIdSpillEnd:31875 case IrInstSrcIdSpillEnd:
31437 case IrInstSrcIdWasmMemorySize:31876 case IrInstSrcIdWasmMemorySize:
31877 case IrInstSrcIdSrc:
31438 return false;31878 return false;
3143931879
31440 case IrInstSrcIdAsm:31880 case IrInstSrcIdAsm:
src/ir_print.cpp+64-54
...@@ -5,6 +5,7 @@...@@ -5,6 +5,7 @@
5 * See http://opensource.org/licenses/MIT5 * See http://opensource.org/licenses/MIT
6 */6 */
77
8#include "all_types.hpp"
8#include "analyze.hpp"9#include "analyze.hpp"
9#include "ir.hpp"10#include "ir.hpp"
10#include "ir_print.hpp"11#include "ir_print.hpp"
...@@ -55,6 +56,36 @@ struct IrPrintGen {...@@ -55,6 +56,36 @@ struct IrPrintGen {
55static void ir_print_other_inst_src(IrPrintSrc *irp, IrInstSrc *inst);56static void ir_print_other_inst_src(IrPrintSrc *irp, IrInstSrc *inst);
56static void ir_print_other_inst_gen(IrPrintGen *irp, IrInstGen *inst);57static void ir_print_other_inst_gen(IrPrintGen *irp, IrInstGen *inst);
5758
59static void ir_print_call_modifier(FILE *f, CallModifier modifier) {
60 switch (modifier) {
61 case CallModifierNone:
62 break;
63 case CallModifierNoSuspend:
64 fprintf(f, "nosuspend ");
65 break;
66 case CallModifierAsync:
67 fprintf(f, "async ");
68 break;
69 case CallModifierNeverTail:
70 fprintf(f, "notail ");
71 break;
72 case CallModifierNeverInline:
73 fprintf(f, "noinline ");
74 break;
75 case CallModifierAlwaysTail:
76 fprintf(f, "tail ");
77 break;
78 case CallModifierAlwaysInline:
79 fprintf(f, "inline ");
80 break;
81 case CallModifierCompileTime:
82 fprintf(f, "comptime ");
83 break;
84 case CallModifierBuiltin:
85 zig_unreachable();
86 }
87}
88
58const char* ir_inst_src_type_str(IrInstSrcId id) {89const char* ir_inst_src_type_str(IrInstSrcId id) {
59 switch (id) {90 switch (id) {
60 case IrInstSrcIdInvalid:91 case IrInstSrcIdInvalid:
...@@ -97,6 +128,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {...@@ -97,6 +128,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
97 return "SrcVarPtr";128 return "SrcVarPtr";
98 case IrInstSrcIdCallExtra:129 case IrInstSrcIdCallExtra:
99 return "SrcCallExtra";130 return "SrcCallExtra";
131 case IrInstSrcIdAsyncCallExtra:
132 return "SrcAsyncCallExtra";
100 case IrInstSrcIdCall:133 case IrInstSrcIdCall:
101 return "SrcCall";134 return "SrcCall";
102 case IrInstSrcIdCallArgs:135 case IrInstSrcIdCallArgs:
...@@ -325,6 +358,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {...@@ -325,6 +358,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
325 return "SrcWasmMemorySize";358 return "SrcWasmMemorySize";
326 case IrInstSrcIdWasmMemoryGrow:359 case IrInstSrcIdWasmMemoryGrow:
327 return "SrcWasmMemoryGrow";360 return "SrcWasmMemoryGrow";
361 case IrInstSrcIdSrc:
362 return "SrcSrc";
328 }363 }
329 zig_unreachable();364 zig_unreachable();
330}365}
...@@ -849,6 +884,23 @@ static void ir_print_call_extra(IrPrintSrc *irp, IrInstSrcCallExtra *instruction...@@ -849,6 +884,23 @@ static void ir_print_call_extra(IrPrintSrc *irp, IrInstSrcCallExtra *instruction
849 ir_print_result_loc(irp, instruction->result_loc);884 ir_print_result_loc(irp, instruction->result_loc);
850}885}
851886
887static void ir_print_async_call_extra(IrPrintSrc *irp, IrInstSrcAsyncCallExtra *instruction) {
888 fprintf(irp->f, "modifier=");
889 ir_print_call_modifier(irp->f, instruction->modifier);
890 fprintf(irp->f, ", fn=");
891 ir_print_other_inst_src(irp, instruction->fn_ref);
892 if (instruction->ret_ptr != nullptr) {
893 fprintf(irp->f, ", ret_ptr=");
894 ir_print_other_inst_src(irp, instruction->ret_ptr);
895 }
896 fprintf(irp->f, ", new_stack=");
897 ir_print_other_inst_src(irp, instruction->new_stack);
898 fprintf(irp->f, ", args=");
899 ir_print_other_inst_src(irp, instruction->args);
900 fprintf(irp->f, ", result=");
901 ir_print_result_loc(irp, instruction->result_loc);
902}
903
852static void ir_print_call_args(IrPrintSrc *irp, IrInstSrcCallArgs *instruction) {904static void ir_print_call_args(IrPrintSrc *irp, IrInstSrcCallArgs *instruction) {
853 fprintf(irp->f, "opts=");905 fprintf(irp->f, "opts=");
854 ir_print_other_inst_src(irp, instruction->options);906 ir_print_other_inst_src(irp, instruction->options);
...@@ -866,33 +918,7 @@ static void ir_print_call_args(IrPrintSrc *irp, IrInstSrcCallArgs *instruction)...@@ -866,33 +918,7 @@ static void ir_print_call_args(IrPrintSrc *irp, IrInstSrcCallArgs *instruction)
866}918}
867919
868static void ir_print_call_src(IrPrintSrc *irp, IrInstSrcCall *call_instruction) {920static void ir_print_call_src(IrPrintSrc *irp, IrInstSrcCall *call_instruction) {
869 switch (call_instruction->modifier) {921 ir_print_call_modifier(irp->f, call_instruction->modifier);
870 case CallModifierNone:
871 break;
872 case CallModifierNoSuspend:
873 fprintf(irp->f, "nosuspend ");
874 break;
875 case CallModifierAsync:
876 fprintf(irp->f, "async ");
877 break;
878 case CallModifierNeverTail:
879 fprintf(irp->f, "notail ");
880 break;
881 case CallModifierNeverInline:
882 fprintf(irp->f, "noinline ");
883 break;
884 case CallModifierAlwaysTail:
885 fprintf(irp->f, "tail ");
886 break;
887 case CallModifierAlwaysInline:
888 fprintf(irp->f, "inline ");
889 break;
890 case CallModifierCompileTime:
891 fprintf(irp->f, "comptime ");
892 break;
893 case CallModifierBuiltin:
894 zig_unreachable();
895 }
896 if (call_instruction->fn_entry) {922 if (call_instruction->fn_entry) {
897 fprintf(irp->f, "%s", buf_ptr(&call_instruction->fn_entry->symbol_name));923 fprintf(irp->f, "%s", buf_ptr(&call_instruction->fn_entry->symbol_name));
898 } else {924 } else {
...@@ -911,33 +937,7 @@ static void ir_print_call_src(IrPrintSrc *irp, IrInstSrcCall *call_instruction)...@@ -911,33 +937,7 @@ static void ir_print_call_src(IrPrintSrc *irp, IrInstSrcCall *call_instruction)
911}937}
912938
913static void ir_print_call_gen(IrPrintGen *irp, IrInstGenCall *call_instruction) {939static void ir_print_call_gen(IrPrintGen *irp, IrInstGenCall *call_instruction) {
914 switch (call_instruction->modifier) {940 ir_print_call_modifier(irp->f, call_instruction->modifier);
915 case CallModifierNone:
916 break;
917 case CallModifierNoSuspend:
918 fprintf(irp->f, "nosuspend ");
919 break;
920 case CallModifierAsync:
921 fprintf(irp->f, "async ");
922 break;
923 case CallModifierNeverTail:
924 fprintf(irp->f, "notail ");
925 break;
926 case CallModifierNeverInline:
927 fprintf(irp->f, "noinline ");
928 break;
929 case CallModifierAlwaysTail:
930 fprintf(irp->f, "tail ");
931 break;
932 case CallModifierAlwaysInline:
933 fprintf(irp->f, "inline ");
934 break;
935 case CallModifierCompileTime:
936 fprintf(irp->f, "comptime ");
937 break;
938 case CallModifierBuiltin:
939 zig_unreachable();
940 }
941 if (call_instruction->fn_entry) {941 if (call_instruction->fn_entry) {
942 fprintf(irp->f, "%s", buf_ptr(&call_instruction->fn_entry->symbol_name));942 fprintf(irp->f, "%s", buf_ptr(&call_instruction->fn_entry->symbol_name));
943 } else {943 } else {
...@@ -1744,6 +1744,10 @@ static void ir_print_wasm_memory_grow(IrPrintGen *irp, IrInstGenWasmMemoryGrow *...@@ -1744,6 +1744,10 @@ static void ir_print_wasm_memory_grow(IrPrintGen *irp, IrInstGenWasmMemoryGrow *
1744 fprintf(irp->f, ")");1744 fprintf(irp->f, ")");
1745}1745}
17461746
1747static void ir_print_builtin_src(IrPrintSrc *irp, IrInstSrcSrc *instruction) {
1748 fprintf(irp->f, "@src()");
1749}
1750
1747static void ir_print_memset(IrPrintSrc *irp, IrInstSrcMemset *instruction) {1751static void ir_print_memset(IrPrintSrc *irp, IrInstSrcMemset *instruction) {
1748 fprintf(irp->f, "@memset(");1752 fprintf(irp->f, "@memset(");
1749 ir_print_other_inst_src(irp, instruction->dest_ptr);1753 ir_print_other_inst_src(irp, instruction->dest_ptr);
...@@ -2613,6 +2617,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai...@@ -2613,6 +2617,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
2613 case IrInstSrcIdCallExtra:2617 case IrInstSrcIdCallExtra:
2614 ir_print_call_extra(irp, (IrInstSrcCallExtra *)instruction);2618 ir_print_call_extra(irp, (IrInstSrcCallExtra *)instruction);
2615 break;2619 break;
2620 case IrInstSrcIdAsyncCallExtra:
2621 ir_print_async_call_extra(irp, (IrInstSrcAsyncCallExtra *)instruction);
2622 break;
2616 case IrInstSrcIdCall:2623 case IrInstSrcIdCall:
2617 ir_print_call_src(irp, (IrInstSrcCall *)instruction);2624 ir_print_call_src(irp, (IrInstSrcCall *)instruction);
2618 break;2625 break;
...@@ -2994,6 +3001,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai...@@ -2994,6 +3001,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai
2994 case IrInstSrcIdWasmMemoryGrow:3001 case IrInstSrcIdWasmMemoryGrow:
2995 ir_print_wasm_memory_grow(irp, (IrInstSrcWasmMemoryGrow *)instruction);3002 ir_print_wasm_memory_grow(irp, (IrInstSrcWasmMemoryGrow *)instruction);
2996 break;3003 break;
3004 case IrInstSrcIdSrc:
3005 ir_print_builtin_src(irp, (IrInstSrcSrc *)instruction);
3006 break;
2997 }3007 }
2998 fprintf(irp->f, "\n");3008 fprintf(irp->f, "\n");
2999}3009}
src/list.hpp+3
...@@ -19,6 +19,9 @@ struct ZigList {...@@ -19,6 +19,9 @@ struct ZigList {
19 ensure_capacity(length + 1);19 ensure_capacity(length + 1);
20 items[length++] = item;20 items[length++] = item;
21 }21 }
22 void append_assuming_capacity(const T& item) {
23 items[length++] = item;
24 }
22 // remember that the pointer to this item is invalid after you25 // remember that the pointer to this item is invalid after you
23 // modify the length of the list26 // modify the length of the list
24 const T & at(size_t index) const {27 const T & at(size_t index) const {
src/os.cpp+213-29
...@@ -6,8 +6,13 @@...@@ -6,8 +6,13 @@
6 */6 */
77
8#include "os.hpp"8#include "os.hpp"
9#include "buffer.hpp"
10#include "heap.hpp"
9#include "util.hpp"11#include "util.hpp"
10#include "error.hpp"12#include "error.hpp"
13#include "util_base.hpp"
14#include <stdint.h>
15#include <stdio.h>
1116
12#if defined(_WIN32)17#if defined(_WIN32)
1318
...@@ -73,6 +78,8 @@ typedef SSIZE_T ssize_t;...@@ -73,6 +78,8 @@ typedef SSIZE_T ssize_t;
73#endif78#endif
7479
75#if defined(ZIG_OS_WINDOWS)80#if defined(ZIG_OS_WINDOWS)
81static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le);
82static size_t utf8_to_utf16le(WCHAR *utf16_le, Slice<uint8_t> utf8);
76static uint64_t windows_perf_freq;83static uint64_t windows_perf_freq;
77#elif defined(__MACH__)84#elif defined(__MACH__)
78static clock_serv_t macos_calendar_clock;85static clock_serv_t macos_calendar_clock;
...@@ -148,15 +155,21 @@ static void os_spawn_process_windows(ZigList<const char *> &args, Termination *t...@@ -148,15 +155,21 @@ static void os_spawn_process_windows(ZigList<const char *> &args, Termination *t
148 os_windows_create_command_line(&command_line, args);155 os_windows_create_command_line(&command_line, args);
149156
150 PROCESS_INFORMATION piProcInfo = {0};157 PROCESS_INFORMATION piProcInfo = {0};
151 STARTUPINFO siStartInfo = {0};158 STARTUPINFOW siStartInfo = {0};
152 siStartInfo.cb = sizeof(STARTUPINFO);159 siStartInfo.cb = sizeof(STARTUPINFOW);
153160
154 const char *exe = args.at(0);161 Slice<uint8_t> exe_slice = str(args.at(0));
155 BOOL success = CreateProcessA(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr,162 auto exe_utf16_slice = Slice<WCHAR>::alloc(exe_slice.len + 1);
163 exe_utf16_slice.ptr[utf8_to_utf16le(exe_utf16_slice.ptr, exe_slice)] = 0;
164
165 auto command_line_utf16 = Slice<WCHAR>::alloc(buf_len(&command_line) + 1);
166 command_line_utf16.ptr[utf8_to_utf16le(command_line_utf16.ptr, buf_to_slice(&command_line))] = 0;
167
168 BOOL success = CreateProcessW(exe_utf16_slice.ptr, command_line_utf16.ptr, nullptr, nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr,
156 &siStartInfo, &piProcInfo);169 &siStartInfo, &piProcInfo);
157170
158 if (!success) {171 if (!success) {
159 zig_panic("CreateProcess failed. exe: %s command_line: %s", exe, buf_ptr(&command_line));172 zig_panic("CreateProcess failed. exe: %s command_line: %s", args.at(0), buf_ptr(&command_line));
160 }173 }
161174
162 WaitForSingleObject(piProcInfo.hProcess, INFINITE);175 WaitForSingleObject(piProcInfo.hProcess, INFINITE);
...@@ -269,11 +282,13 @@ void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) {...@@ -269,11 +282,13 @@ void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) {
269282
270Error os_path_real(Buf *rel_path, Buf *out_abs_path) {283Error os_path_real(Buf *rel_path, Buf *out_abs_path) {
271#if defined(ZIG_OS_WINDOWS)284#if defined(ZIG_OS_WINDOWS)
272 buf_resize(out_abs_path, 4096);285 PathSpace rel_path_space = slice_to_prefixed_file_w(buf_to_slice(rel_path));
273 if (_fullpath(buf_ptr(out_abs_path), buf_ptr(rel_path), buf_len(out_abs_path)) == nullptr) {286 PathSpace out_abs_path_space;
274 zig_panic("_fullpath failed");287
288 if (_wfullpath(&out_abs_path_space.data.items[0], &rel_path_space.data.items[0], PATH_MAX_WIDE) == nullptr) {
289 zig_panic("_wfullpath failed");
275 }290 }
276 buf_resize(out_abs_path, strlen(buf_ptr(out_abs_path)));291 utf16le_ptr_to_utf8(out_abs_path, &out_abs_path_space.data.items[0]);
277 return ErrorNone;292 return ErrorNone;
278#elif defined(ZIG_OS_POSIX)293#elif defined(ZIG_OS_POSIX)
279 buf_resize(out_abs_path, PATH_MAX + 1);294 buf_resize(out_abs_path, PATH_MAX + 1);
...@@ -773,7 +788,8 @@ Error os_fetch_file(FILE *f, Buf *out_buf) {...@@ -773,7 +788,8 @@ Error os_fetch_file(FILE *f, Buf *out_buf) {
773788
774Error os_file_exists(Buf *full_path, bool *result) {789Error os_file_exists(Buf *full_path, bool *result) {
775#if defined(ZIG_OS_WINDOWS)790#if defined(ZIG_OS_WINDOWS)
776 *result = GetFileAttributes(buf_ptr(full_path)) != INVALID_FILE_ATTRIBUTES;791 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
792 *result = GetFileAttributesW(&path_space.data.items[0]) != INVALID_FILE_ATTRIBUTES;
777 return ErrorNone;793 return ErrorNone;
778#else794#else
779 *result = access(buf_ptr(full_path), F_OK) != -1;795 *result = access(buf_ptr(full_path), F_OK) != -1;
...@@ -1021,7 +1037,12 @@ Error os_exec_process(ZigList<const char *> &args,...@@ -1021,7 +1037,12 @@ Error os_exec_process(ZigList<const char *> &args,
1021}1037}
10221038
1023Error os_write_file(Buf *full_path, Buf *contents) {1039Error os_write_file(Buf *full_path, Buf *contents) {
1040#if defined(ZIG_OS_WINDOWS)
1041 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
1042 FILE *f = _wfopen(&path_space.data.items[0], L"wb");
1043#else
1024 FILE *f = fopen(buf_ptr(full_path), "wb");1044 FILE *f = fopen(buf_ptr(full_path), "wb");
1045#endif
1025 if (!f) {1046 if (!f) {
1026 zig_panic("os_write_file failed for %s", buf_ptr(full_path));1047 zig_panic("os_write_file failed for %s", buf_ptr(full_path));
1027 }1048 }
...@@ -1056,7 +1077,12 @@ static Error copy_open_files(FILE *src_f, FILE *dest_f) {...@@ -1056,7 +1077,12 @@ static Error copy_open_files(FILE *src_f, FILE *dest_f) {
1056Error os_dump_file(Buf *src_path, FILE *dest_file) {1077Error os_dump_file(Buf *src_path, FILE *dest_file) {
1057 Error err;1078 Error err;
10581079
1080#if defined(ZIG_OS_WINDOWS)
1081 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(src_path));
1082 FILE *src_f = _wfopen(&path_space.data.items[0], L"rb");
1083#else
1059 FILE *src_f = fopen(buf_ptr(src_path), "rb");1084 FILE *src_f = fopen(buf_ptr(src_path), "rb");
1085#endif
1060 if (!src_f) {1086 if (!src_f) {
1061 int err = errno;1087 int err = errno;
1062 if (err == ENOENT) {1088 if (err == ENOENT) {
...@@ -1173,7 +1199,12 @@ Error os_update_file(Buf *src_path, Buf *dst_path) {...@@ -1173,7 +1199,12 @@ Error os_update_file(Buf *src_path, Buf *dst_path) {
1173}1199}
11741200
1175Error os_copy_file(Buf *src_path, Buf *dest_path) {1201Error os_copy_file(Buf *src_path, Buf *dest_path) {
1202#if defined(ZIG_OS_WINDOWS)
1203 PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path));
1204 FILE *src_f = _wfopen(&src_path_space.data.items[0], L"rb");
1205#else
1176 FILE *src_f = fopen(buf_ptr(src_path), "rb");1206 FILE *src_f = fopen(buf_ptr(src_path), "rb");
1207#endif
1177 if (!src_f) {1208 if (!src_f) {
1178 int err = errno;1209 int err = errno;
1179 if (err == ENOENT) {1210 if (err == ENOENT) {
...@@ -1184,7 +1215,12 @@ Error os_copy_file(Buf *src_path, Buf *dest_path) {...@@ -1184,7 +1215,12 @@ Error os_copy_file(Buf *src_path, Buf *dest_path) {
1184 return ErrorFileSystem;1215 return ErrorFileSystem;
1185 }1216 }
1186 }1217 }
1218#if defined(ZIG_OS_WINDOWS)
1219 PathSpace dest_path_space = slice_to_prefixed_file_w(buf_to_slice(dest_path));
1220 FILE *dest_f = _wfopen(&dest_path_space.data.items[0], L"wb");
1221#else
1187 FILE *dest_f = fopen(buf_ptr(dest_path), "wb");1222 FILE *dest_f = fopen(buf_ptr(dest_path), "wb");
1223#endif
1188 if (!dest_f) {1224 if (!dest_f) {
1189 int err = errno;1225 int err = errno;
1190 if (err == ENOENT) {1226 if (err == ENOENT) {
...@@ -1205,7 +1241,12 @@ Error os_copy_file(Buf *src_path, Buf *dest_path) {...@@ -1205,7 +1241,12 @@ Error os_copy_file(Buf *src_path, Buf *dest_path) {
1205}1241}
12061242
1207Error os_fetch_file_path(Buf *full_path, Buf *out_contents) {1243Error os_fetch_file_path(Buf *full_path, Buf *out_contents) {
1244#if defined(ZIG_OS_WINDOWS)
1245 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
1246 FILE *f = _wfopen(&path_space.data.items[0], L"rb");
1247#else
1208 FILE *f = fopen(buf_ptr(full_path), "rb");1248 FILE *f = fopen(buf_ptr(full_path), "rb");
1249#endif
1209 if (!f) {1250 if (!f) {
1210 switch (errno) {1251 switch (errno) {
1211 case EACCES:1252 case EACCES:
...@@ -1230,11 +1271,11 @@ Error os_fetch_file_path(Buf *full_path, Buf *out_contents) {...@@ -1230,11 +1271,11 @@ Error os_fetch_file_path(Buf *full_path, Buf *out_contents) {
12301271
1231Error os_get_cwd(Buf *out_cwd) {1272Error os_get_cwd(Buf *out_cwd) {
1232#if defined(ZIG_OS_WINDOWS)1273#if defined(ZIG_OS_WINDOWS)
1233 char buf[4096];1274 PathSpace path_space;
1234 if (GetCurrentDirectory(4096, buf) == 0) {1275 if (GetCurrentDirectoryW(PATH_MAX_WIDE, &path_space.data.items[0]) == 0) {
1235 zig_panic("GetCurrentDirectory failed");1276 zig_panic("GetCurrentDirectory failed");
1236 }1277 }
1237 buf_init_from_str(out_cwd, buf);1278 utf16le_ptr_to_utf8(out_cwd, &path_space.data.items[0]);
1238 return ErrorNone;1279 return ErrorNone;
1239#elif defined(ZIG_OS_POSIX)1280#elif defined(ZIG_OS_POSIX)
1240 char buf[PATH_MAX];1281 char buf[PATH_MAX];
...@@ -1330,7 +1371,9 @@ Error os_rename(Buf *src_path, Buf *dest_path) {...@@ -1330,7 +1371,9 @@ Error os_rename(Buf *src_path, Buf *dest_path) {
1330 return ErrorNone;1371 return ErrorNone;
1331 }1372 }
1332#if defined(ZIG_OS_WINDOWS)1373#if defined(ZIG_OS_WINDOWS)
1333 if (!MoveFileExA(buf_ptr(src_path), buf_ptr(dest_path), MOVEFILE_REPLACE_EXISTING)) {1374 PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path));
1375 PathSpace dest_path_space = slice_to_prefixed_file_w(buf_to_slice(dest_path));
1376 if (!MoveFileExW(&src_path_space.data.items[0], &dest_path_space.data.items[0], MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {
1334 return ErrorFileSystem;1377 return ErrorFileSystem;
1335 }1378 }
1336#else1379#else
...@@ -1426,7 +1469,15 @@ Error os_make_path(Buf *path) {...@@ -1426,7 +1469,15 @@ Error os_make_path(Buf *path) {
14261469
1427Error os_make_dir(Buf *path) {1470Error os_make_dir(Buf *path) {
1428#if defined(ZIG_OS_WINDOWS)1471#if defined(ZIG_OS_WINDOWS)
1429 if (!CreateDirectory(buf_ptr(path), NULL)) {1472 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(path));
1473 if (memEql(buf_to_slice(path), str("C:\\dev\\tést"))) {
1474 for (size_t i = 0; i < path_space.len; i++) {
1475 fprintf(stderr, "%d ", path_space.data.items[i]);
1476 }
1477 fprintf(stderr, "\n");
1478 }
1479
1480 if (!CreateDirectoryW(&path_space.data.items[0], NULL)) {
1430 if (GetLastError() == ERROR_ALREADY_EXISTS)1481 if (GetLastError() == ERROR_ALREADY_EXISTS)
1431 return ErrorPathAlreadyExists;1482 return ErrorPathAlreadyExists;
1432 if (GetLastError() == ERROR_PATH_NOT_FOUND)1483 if (GetLastError() == ERROR_PATH_NOT_FOUND)
...@@ -1531,18 +1582,13 @@ int os_init(void) {...@@ -1531,18 +1582,13 @@ int os_init(void) {
15311582
1532Error os_self_exe_path(Buf *out_path) {1583Error os_self_exe_path(Buf *out_path) {
1533#if defined(ZIG_OS_WINDOWS)1584#if defined(ZIG_OS_WINDOWS)
1534 buf_resize(out_path, 256);1585 PathSpace path_space;
1535 for (;;) {1586 DWORD copied_amt = GetModuleFileNameW(nullptr, &path_space.data.items[0], PATH_MAX_WIDE);
1536 DWORD copied_amt = GetModuleFileName(nullptr, buf_ptr(out_path), buf_len(out_path));1587 if (copied_amt <= 0) {
1537 if (copied_amt <= 0) {1588 return ErrorFileNotFound;
1538 return ErrorFileNotFound;
1539 }
1540 if (copied_amt < buf_len(out_path)) {
1541 buf_resize(out_path, copied_amt);
1542 return ErrorNone;
1543 }
1544 buf_resize(out_path, buf_len(out_path) * 2);
1545 }1589 }
1590 utf16le_ptr_to_utf8(out_path, &path_space.data.items[0]);
1591 return ErrorNone;
15461592
1547#elif defined(ZIG_OS_DARWIN)1593#elif defined(ZIG_OS_DARWIN)
1548 // How long is the executable's path?1594 // How long is the executable's path?
...@@ -1719,6 +1765,15 @@ static uint8_t utf8CodepointSequenceLength(uint32_t c) {...@@ -1719,6 +1765,15 @@ static uint8_t utf8CodepointSequenceLength(uint32_t c) {
1719 zig_unreachable();1765 zig_unreachable();
1720}1766}
17211767
1768// Ported from std.unicode.utf8ByteSequenceLength
1769static uint8_t utf8ByteSequenceLength(uint8_t first_byte) {
1770 if (first_byte < 0b10000000) return 1;
1771 if ((first_byte & 0b11100000) == 0b11000000) return 2;
1772 if ((first_byte & 0b11110000) == 0b11100000) return 3;
1773 if ((first_byte & 0b11111000) == 0b11110000) return 4;
1774 zig_unreachable();
1775}
1776
1722// Ported from std/unicode.zig1777// Ported from std/unicode.zig
1723static size_t utf8Encode(uint32_t c, Slice<uint8_t> out) {1778static size_t utf8Encode(uint32_t c, Slice<uint8_t> out) {
1724 size_t length = utf8CodepointSequenceLength(c);1779 size_t length = utf8CodepointSequenceLength(c);
...@@ -1753,6 +1808,80 @@ static size_t utf8Encode(uint32_t c, Slice<uint8_t> out) {...@@ -1753,6 +1808,80 @@ static size_t utf8Encode(uint32_t c, Slice<uint8_t> out) {
1753 return length;1808 return length;
1754}1809}
17551810
1811// Ported from std.unicode.utf8Decode2
1812static uint32_t utf8Decode2(Slice<uint8_t> bytes) {
1813 assert(bytes.len == 2);
1814 assert((bytes.at(0) & 0b11100000) == 0b11000000);
1815
1816 uint32_t value = bytes.at(0) & 0b00011111;
1817 assert((bytes.at(1) & 0b11000000) == 0b10000000);
1818 value <<= 6;
1819 value |= bytes.at(1) & 0b00111111;
1820
1821 assert(value >= 0x80);
1822 return value;
1823}
1824
1825// Ported from std.unicode.utf8Decode3
1826static uint32_t utf8Decode3(Slice<uint8_t> bytes) {
1827 assert(bytes.len == 3);
1828 assert((bytes.at(0) & 0b11110000) == 0b11100000);
1829
1830 uint32_t value = bytes.at(0) & 0b00001111;
1831 assert((bytes.at(1) & 0b11000000) == 0b10000000);
1832 value <<= 6;
1833 value |= bytes.at(1) & 0b00111111;
1834
1835 assert((bytes.at(2) & 0b11000000) == 0b10000000);
1836 value <<= 6;
1837 value |= bytes.at(2) & 0b00111111;
1838
1839 assert(value >= 0x80);
1840 assert(value < 0xd800 || value > 0xdfff);
1841 return value;
1842}
1843
1844// Ported from std.unicode.utf8Decode4
1845static uint32_t utf8Decode4(Slice<uint8_t> bytes) {
1846 assert(bytes.len == 4);
1847 assert((bytes.at(0) & 0b11111000) == 0b11110000);
1848
1849 uint32_t value = bytes.at(0) & 0b00000111;
1850 assert((bytes.at(1) & 0b11000000) == 0b10000000);
1851 value <<= 6;
1852 value |= bytes.at(1) & 0b00111111;
1853
1854 assert((bytes.at(2) & 0b11000000) == 0b10000000);
1855 value <<= 6;
1856 value |= bytes.at(2) & 0b00111111;
1857
1858 assert((bytes.at(3) & 0b11000000) == 0b10000000);
1859 value <<= 6;
1860 value |= bytes.at(3) & 0b00111111;
1861
1862 assert(value >= 0x10000 && value <= 0x10FFFF);
1863 return value;
1864}
1865
1866// Ported from std.unicode.utf8Decode
1867static uint32_t utf8Decode(Slice<uint8_t> bytes) {
1868 switch (bytes.len) {
1869 case 1:
1870 return bytes.at(0);
1871 break;
1872 case 2:
1873 return utf8Decode2(bytes);
1874 break;
1875 case 3:
1876 return utf8Decode3(bytes);
1877 break;
1878 case 4:
1879 return utf8Decode4(bytes);
1880 break;
1881 default:
1882 zig_unreachable();
1883 }
1884}
1756// Ported from std.unicode.utf16leToUtf8Alloc1885// Ported from std.unicode.utf16leToUtf8Alloc
1757static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le) {1886static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le) {
1758 // optimistically guess that it will all be ascii.1887 // optimistically guess that it will all be ascii.
...@@ -1770,6 +1899,60 @@ static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le) {...@@ -1770,6 +1899,60 @@ static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le) {
1770 out_index += utf8_len;1899 out_index += utf8_len;
1771 }1900 }
1772}1901}
1902
1903// Ported from std.unicode.utf8ToUtf16Le
1904static size_t utf8_to_utf16le(WCHAR *utf16_le, Slice<uint8_t> utf8) {
1905 size_t dest_i = 0;
1906 size_t src_i = 0;
1907 while (src_i < utf8.len) {
1908 uint8_t n = utf8ByteSequenceLength(utf8.at(src_i));
1909 size_t next_src_i = src_i + n;
1910 uint32_t codepoint = utf8Decode(utf8.slice(src_i, next_src_i));
1911 if (codepoint < 0x10000) {
1912 utf16_le[dest_i] = codepoint;
1913 dest_i += 1;
1914 } else {
1915 WCHAR high = ((codepoint - 0x10000) >> 10) + 0xD800;
1916 WCHAR low = (codepoint & 0x3FF) + 0xDC00;
1917 utf16_le[dest_i] = high;
1918 utf16_le[dest_i + 1] = low;
1919 dest_i += 2;
1920 }
1921 src_i = next_src_i;
1922 }
1923 return dest_i;
1924}
1925
1926// Ported from std.os.windows.sliceToPrefixedFileW
1927PathSpace slice_to_prefixed_file_w(Slice<uint8_t> path) {
1928 PathSpace path_space;
1929 for (size_t idx = 0; idx < path.len; idx++) {
1930 assert(path.ptr[idx] != '*' && path.ptr[idx] != '?' && path.ptr[idx] != '"' &&
1931 path.ptr[idx] != '<' && path.ptr[idx] != '>' && path.ptr[idx] != '|');
1932 }
1933
1934 size_t start_index;
1935 if (memStartsWith(path, str("\\?")) || !isAbsoluteWindows(path)) {
1936 start_index = 0;
1937 } else {
1938 static WCHAR prefix[4] = { u'\\', u'?', u'?', u'\\' };
1939 memCopy(path_space.data.slice(), Slice<WCHAR> { prefix, 4 });
1940 start_index = 4;
1941 }
1942
1943 path_space.len = start_index + utf8_to_utf16le(path_space.data.slice().sliceFrom(start_index).ptr, path);
1944 assert(path_space.len <= path_space.data.len);
1945
1946 Slice<WCHAR> path_slice = path_space.data.slice().slice(0, path_space.len);
1947 for (size_t elem_idx = 0; elem_idx < path_slice.len; elem_idx += 1) {
1948 if (path_slice.at(elem_idx) == '/') {
1949 path_slice.at(elem_idx) = '\\';
1950 }
1951 }
1952
1953 path_space.data.items[path_space.len] = 0;
1954 return path_space;
1955}
1773#endif1956#endif
17741957
1775// Ported from std.os.getAppDataDir1958// Ported from std.os.getAppDataDir
...@@ -1862,8 +2045,8 @@ Error os_self_exe_shared_libs(ZigList<Buf *> &paths) {...@@ -1862,8 +2045,8 @@ Error os_self_exe_shared_libs(ZigList<Buf *> &paths) {
18622045
1863Error os_file_open_rw(Buf *full_path, OsFile *out_file, OsFileAttr *attr, bool need_write, uint32_t mode) {2046Error os_file_open_rw(Buf *full_path, OsFile *out_file, OsFileAttr *attr, bool need_write, uint32_t mode) {
1864#if defined(ZIG_OS_WINDOWS)2047#if defined(ZIG_OS_WINDOWS)
1865 // TODO use CreateFileW2048 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
1866 HANDLE result = CreateFileA(buf_ptr(full_path),2049 HANDLE result = CreateFileW(&path_space.data.items[0],
1867 need_write ? (GENERIC_READ|GENERIC_WRITE) : GENERIC_READ,2050 need_write ? (GENERIC_READ|GENERIC_WRITE) : GENERIC_READ,
1868 need_write ? 0 : FILE_SHARE_READ,2051 need_write ? 0 : FILE_SHARE_READ,
1869 nullptr,2052 nullptr,
...@@ -1967,8 +2150,9 @@ Error os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_...@@ -1967,8 +2150,9 @@ Error os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_
19672150
1968Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) {2151Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) {
1969#if defined(ZIG_OS_WINDOWS)2152#if defined(ZIG_OS_WINDOWS)
2153 PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
1970 for (;;) {2154 for (;;) {
1971 HANDLE result = CreateFileA(buf_ptr(full_path), GENERIC_READ | GENERIC_WRITE,2155 HANDLE result = CreateFileW(&path_space.data.items[0], GENERIC_READ | GENERIC_WRITE,
1972 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);2156 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
19732157
1974 if (result == INVALID_HANDLE_VALUE) {2158 if (result == INVALID_HANDLE_VALUE) {
src/os.hpp+8
...@@ -155,4 +155,12 @@ Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname)...@@ -155,4 +155,12 @@ Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname)
155155
156Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths);156Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths);
157157
158const size_t PATH_MAX_WIDE = 32767;
159
160struct PathSpace {
161 Array<wchar_t, PATH_MAX_WIDE> data;
162 size_t len;
163};
164
165PathSpace slice_to_prefixed_file_w(Slice<uint8_t> path);
158#endif166#endif
src/parser.cpp+14-14
...@@ -786,7 +786,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B...@@ -786,7 +786,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
786 return nullptr;786 return nullptr;
787}787}
788788
789// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)789// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype / TypeExpr)
790static AstNode *ast_parse_fn_proto(ParseContext *pc) {790static AstNode *ast_parse_fn_proto(ParseContext *pc) {
791 Token *first = eat_token_if(pc, TokenIdKeywordFn);791 Token *first = eat_token_if(pc, TokenIdKeywordFn);
792 if (first == nullptr) {792 if (first == nullptr) {
...@@ -801,10 +801,10 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {...@@ -801,10 +801,10 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
801 AstNode *align_expr = ast_parse_byte_align(pc);801 AstNode *align_expr = ast_parse_byte_align(pc);
802 AstNode *section_expr = ast_parse_link_section(pc);802 AstNode *section_expr = ast_parse_link_section(pc);
803 AstNode *callconv_expr = ast_parse_callconv(pc);803 AstNode *callconv_expr = ast_parse_callconv(pc);
804 Token *var = eat_token_if(pc, TokenIdKeywordVar);804 Token *anytype = eat_token_if(pc, TokenIdKeywordAnyType);
805 Token *exmark = nullptr;805 Token *exmark = nullptr;
806 AstNode *return_type = nullptr;806 AstNode *return_type = nullptr;
807 if (var == nullptr) {807 if (anytype == nullptr) {
808 exmark = eat_token_if(pc, TokenIdBang);808 exmark = eat_token_if(pc, TokenIdBang);
809 return_type = ast_expect(pc, ast_parse_type_expr);809 return_type = ast_expect(pc, ast_parse_type_expr);
810 }810 }
...@@ -816,7 +816,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {...@@ -816,7 +816,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
816 res->data.fn_proto.align_expr = align_expr;816 res->data.fn_proto.align_expr = align_expr;
817 res->data.fn_proto.section_expr = section_expr;817 res->data.fn_proto.section_expr = section_expr;
818 res->data.fn_proto.callconv_expr = callconv_expr;818 res->data.fn_proto.callconv_expr = callconv_expr;
819 res->data.fn_proto.return_var_token = var;819 res->data.fn_proto.return_anytype_token = anytype;
820 res->data.fn_proto.auto_err_set = exmark != nullptr;820 res->data.fn_proto.auto_err_set = exmark != nullptr;
821 res->data.fn_proto.return_type = return_type;821 res->data.fn_proto.return_type = return_type;
822822
...@@ -870,9 +870,9 @@ static AstNode *ast_parse_container_field(ParseContext *pc) {...@@ -870,9 +870,9 @@ static AstNode *ast_parse_container_field(ParseContext *pc) {
870870
871 AstNode *type_expr = nullptr;871 AstNode *type_expr = nullptr;
872 if (eat_token_if(pc, TokenIdColon) != nullptr) {872 if (eat_token_if(pc, TokenIdColon) != nullptr) {
873 Token *var_tok = eat_token_if(pc, TokenIdKeywordVar);873 Token *anytype_tok = eat_token_if(pc, TokenIdKeywordAnyType);
874 if (var_tok != nullptr) {874 if (anytype_tok != nullptr) {
875 type_expr = ast_create_node(pc, NodeTypeVarFieldType, var_tok);875 type_expr = ast_create_node(pc, NodeTypeAnyTypeField, anytype_tok);
876 } else {876 } else {
877 type_expr = ast_expect(pc, ast_parse_type_expr);877 type_expr = ast_expect(pc, ast_parse_type_expr);
878 }878 }
...@@ -2191,14 +2191,14 @@ static AstNode *ast_parse_param_decl(ParseContext *pc) {...@@ -2191,14 +2191,14 @@ static AstNode *ast_parse_param_decl(ParseContext *pc) {
2191}2191}
21922192
2193// ParamType2193// ParamType
2194// <- KEYWORD_var2194// <- KEYWORD_anytype
2195// / DOT32195// / DOT3
2196// / TypeExpr2196// / TypeExpr
2197static AstNode *ast_parse_param_type(ParseContext *pc) {2197static AstNode *ast_parse_param_type(ParseContext *pc) {
2198 Token *var_token = eat_token_if(pc, TokenIdKeywordVar);2198 Token *anytype_token = eat_token_if(pc, TokenIdKeywordAnyType);
2199 if (var_token != nullptr) {2199 if (anytype_token != nullptr) {
2200 AstNode *res = ast_create_node(pc, NodeTypeParamDecl, var_token);2200 AstNode *res = ast_create_node(pc, NodeTypeParamDecl, anytype_token);
2201 res->data.param_decl.var_token = var_token;2201 res->data.param_decl.anytype_token = anytype_token;
2202 return res;2202 return res;
2203 }2203 }
22042204
...@@ -2679,7 +2679,7 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {...@@ -2679,7 +2679,7 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
26792679
2680 if (eat_token_if(pc, TokenIdKeywordAlign) != nullptr) {2680 if (eat_token_if(pc, TokenIdKeywordAlign) != nullptr) {
2681 expect_token(pc, TokenIdLParen);2681 expect_token(pc, TokenIdLParen);
2682 AstNode *align_expr = ast_parse_expr(pc);2682 AstNode *align_expr = ast_expect(pc, ast_parse_expr);
2683 child->data.pointer_type.align_expr = align_expr;2683 child->data.pointer_type.align_expr = align_expr;
2684 if (eat_token_if(pc, TokenIdColon) != nullptr) {2684 if (eat_token_if(pc, TokenIdColon) != nullptr) {
2685 Token *bit_offset_start = expect_token(pc, TokenIdIntLiteral);2685 Token *bit_offset_start = expect_token(pc, TokenIdIntLiteral);
...@@ -3207,7 +3207,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont...@@ -3207,7 +3207,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
3207 visit_field(&node->data.suspend.block, visit, context);3207 visit_field(&node->data.suspend.block, visit, context);
3208 break;3208 break;
3209 case NodeTypeEnumLiteral:3209 case NodeTypeEnumLiteral:
3210 case NodeTypeVarFieldType:3210 case NodeTypeAnyTypeField:
3211 break;3211 break;
3212 }3212 }
3213}3213}
src/softfloat_ext.cpp created+25
...@@ -0,0 +1,25 @@
1#include "softfloat_ext.hpp"
2
3extern "C" {
4 #include "softfloat.h"
5}
6
7void f128M_abs(const float128_t *aPtr, float128_t *zPtr) {
8 float128_t zero_float;
9 ui32_to_f128M(0, &zero_float);
10 if (f128M_lt(aPtr, &zero_float)) {
11 f128M_sub(&zero_float, aPtr, zPtr);
12 } else {
13 *zPtr = *aPtr;
14 }
15}
16
17void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) {
18 float128_t zero_float;
19 ui32_to_f128M(0, &zero_float);
20 if (f128M_lt(aPtr, &zero_float)) {
21 f128M_roundToInt(aPtr, softfloat_round_max, false, zPtr);
22 } else {
23 f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr);
24 }
25}
\ No newline at end of file
src/softfloat_ext.hpp created+9
...@@ -0,0 +1,9 @@
1#ifndef ZIG_SOFTFLOAT_EXT_HPP
2#define ZIG_SOFTFLOAT_EXT_HPP
3
4#include "softfloat_types.h"
5
6void f128M_abs(const float128_t *aPtr, float128_t *zPtr);
7void f128M_trunc(const float128_t *aPtr, float128_t *zPtr);
8
9#endif
\ No newline at end of file
src/tokenizer.cpp+2
...@@ -106,6 +106,7 @@ static const struct ZigKeyword zig_keywords[] = {...@@ -106,6 +106,7 @@ static const struct ZigKeyword zig_keywords[] = {
106 {"allowzero", TokenIdKeywordAllowZero},106 {"allowzero", TokenIdKeywordAllowZero},
107 {"and", TokenIdKeywordAnd},107 {"and", TokenIdKeywordAnd},
108 {"anyframe", TokenIdKeywordAnyFrame},108 {"anyframe", TokenIdKeywordAnyFrame},
109 {"anytype", TokenIdKeywordAnyType},
109 {"asm", TokenIdKeywordAsm},110 {"asm", TokenIdKeywordAsm},
110 {"async", TokenIdKeywordAsync},111 {"async", TokenIdKeywordAsync},
111 {"await", TokenIdKeywordAwait},112 {"await", TokenIdKeywordAwait},
...@@ -1569,6 +1570,7 @@ const char * token_name(TokenId id) {...@@ -1569,6 +1570,7 @@ const char * token_name(TokenId id) {
1569 case TokenIdKeywordAlign: return "align";1570 case TokenIdKeywordAlign: return "align";
1570 case TokenIdKeywordAnd: return "and";1571 case TokenIdKeywordAnd: return "and";
1571 case TokenIdKeywordAnyFrame: return "anyframe";1572 case TokenIdKeywordAnyFrame: return "anyframe";
1573 case TokenIdKeywordAnyType: return "anytype";
1572 case TokenIdKeywordAsm: return "asm";1574 case TokenIdKeywordAsm: return "asm";
1573 case TokenIdKeywordBreak: return "break";1575 case TokenIdKeywordBreak: return "break";
1574 case TokenIdKeywordCatch: return "catch";1576 case TokenIdKeywordCatch: return "catch";
src/tokenizer.hpp+1
...@@ -54,6 +54,7 @@ enum TokenId {...@@ -54,6 +54,7 @@ enum TokenId {
54 TokenIdKeywordAllowZero,54 TokenIdKeywordAllowZero,
55 TokenIdKeywordAnd,55 TokenIdKeywordAnd,
56 TokenIdKeywordAnyFrame,56 TokenIdKeywordAnyFrame,
57 TokenIdKeywordAnyType,
57 TokenIdKeywordAsm,58 TokenIdKeywordAsm,
58 TokenIdKeywordAsync,59 TokenIdKeywordAsync,
59 TokenIdKeywordAwait,60 TokenIdKeywordAwait,
src/util.hpp+1-1
...@@ -159,7 +159,7 @@ struct Slice {...@@ -159,7 +159,7 @@ struct Slice {
159159
160 inline T &at(size_t i) {160 inline T &at(size_t i) {
161 assert(i < len);161 assert(i < len);
162 return &ptr[i];162 return ptr[i];
163 }163 }
164164
165 inline Slice<T> slice(size_t start, size_t end) {165 inline Slice<T> slice(size_t start, size_t end) {
test/cli.zig+27
...@@ -34,6 +34,7 @@ pub fn main() !void {...@@ -34,6 +34,7 @@ pub fn main() !void {
34 testZigInitExe,34 testZigInitExe,
35 testGodboltApi,35 testGodboltApi,
36 testMissingOutputPath,36 testMissingOutputPath,
37 testZigFmt,
37 };38 };
38 for (test_fns) |testFn| {39 for (test_fns) |testFn| {
39 try fs.cwd().deleteTree(dir_path);40 try fs.cwd().deleteTree(dir_path);
...@@ -143,3 +144,29 @@ fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {...@@ -143,3 +144,29 @@ fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {
143 zig_exe, "build-exe", source_path, "--output-dir", output_path,144 zig_exe, "build-exe", source_path, "--output-dir", output_path,
144 });145 });
145}146}
147
148fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
149 _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
150
151 const unformatted_code = " // no reason for indent";
152
153 const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" });
154 try fs.cwd().writeFile(fmt1_zig_path, unformatted_code);
155
156 const run_result1 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path });
157 // stderr should be file path + \n
158 testing.expect(std.mem.startsWith(u8, run_result1.stderr, fmt1_zig_path));
159 testing.expect(run_result1.stderr.len == fmt1_zig_path.len + 1 and run_result1.stderr[run_result1.stderr.len - 1] == '\n');
160
161 const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" });
162 try fs.cwd().writeFile(fmt2_zig_path, unformatted_code);
163
164 const run_result2 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });
165 // running it on the dir, only the new file should be changed
166 testing.expect(std.mem.startsWith(u8, run_result2.stderr, fmt2_zig_path));
167 testing.expect(run_result2.stderr.len == fmt2_zig_path.len + 1 and run_result2.stderr[run_result2.stderr.len - 1] == '\n');
168
169 const run_result3 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });
170 // both files have been formatted, nothing should change now
171 testing.expect(run_result3.stderr.len == 0);
172}
test/compile_errors.zig+143-28
...@@ -2,6 +2,55 @@ const tests = @import("tests.zig");...@@ -2,6 +2,55 @@ const tests = @import("tests.zig");
2const std = @import("std");2const std = @import("std");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.addTest("invalid pointer with @Type",
6 \\export fn entry() void {
7 \\ _ = @Type(.{ .Pointer = .{
8 \\ .size = .One,
9 \\ .is_const = false,
10 \\ .is_volatile = false,
11 \\ .alignment = 1,
12 \\ .child = u8,
13 \\ .is_allowzero = false,
14 \\ .sentinel = 0,
15 \\ }});
16 \\}
17 , &[_][]const u8{
18 "tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers",
19 });
20
21 cases.addTest("int/float conversion to comptime_int/float",
22 \\export fn foo() void {
23 \\ var a: f32 = 2;
24 \\ _ = @floatToInt(comptime_int, a);
25 \\}
26 \\export fn bar() void {
27 \\ var a: u32 = 2;
28 \\ _ = @intToFloat(comptime_float, a);
29 \\}
30 , &[_][]const u8{
31 "tmp.zig:3:35: error: unable to evaluate constant expression",
32 "tmp.zig:3:9: note: referenced here",
33 "tmp.zig:7:37: error: unable to evaluate constant expression",
34 "tmp.zig:7:9: note: referenced here",
35 });
36
37 cases.add("extern variable has no type",
38 \\extern var foo;
39 \\pub export fn entry() void {
40 \\ foo;
41 \\}
42 , &[_][]const u8{
43 "tmp.zig:1:1: error: unable to infer variable type",
44 });
45
46 cases.add("@src outside function",
47 \\comptime {
48 \\ @src();
49 \\}
50 , &[_][]const u8{
51 "tmp.zig:2:5: error: @src outside function",
52 });
53
5 cases.add("call assigned to constant",54 cases.add("call assigned to constant",
6 \\const Foo = struct {55 \\const Foo = struct {
7 \\ x: i32,56 \\ x: i32,
...@@ -9,7 +58,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -9,7 +58,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
9 \\fn foo() Foo {58 \\fn foo() Foo {
10 \\ return .{ .x = 42 };59 \\ return .{ .x = 42 };
11 \\}60 \\}
12 \\fn bar(val: var) Foo {61 \\fn bar(val: anytype) Foo {
13 \\ return .{ .x = val };62 \\ return .{ .x = val };
14 \\}63 \\}
15 \\export fn entry() void {64 \\export fn entry() void {
...@@ -74,17 +123,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -74,17 +123,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
74 \\ _ = @floatToInt(u32, a);123 \\ _ = @floatToInt(u32, a);
75 \\}124 \\}
76 \\export fn qux() void {125 \\export fn qux() void {
77 \\ var a: u32 = 2;126 \\ var a: f32 = 2;
78 \\ _ = @intCast(comptime_int, a);127 \\ _ = @intCast(u32, a);
79 \\}128 \\}
80 , &[_][]const u8{129 , &[_][]const u8{
81 "tmp.zig:3:32: error: expected type 'comptime_int', found 'u32'",130 "tmp.zig:3:32: error: unable to evaluate constant expression",
82 "tmp.zig:3:9: note: referenced here",131 "tmp.zig:3:9: note: referenced here",
83 "tmp.zig:7:21: error: expected float type, found 'u32'",132 "tmp.zig:7:21: error: expected float type, found 'u32'",
84 "tmp.zig:7:9: note: referenced here",133 "tmp.zig:7:9: note: referenced here",
85 "tmp.zig:11:26: error: expected float type, found 'u32'",134 "tmp.zig:11:26: error: expected float type, found 'u32'",
86 "tmp.zig:11:9: note: referenced here",135 "tmp.zig:11:9: note: referenced here",
87 "tmp.zig:15:32: error: expected type 'comptime_int', found 'u32'",136 "tmp.zig:15:23: error: expected integer type, found 'f32'",
88 "tmp.zig:15:9: note: referenced here",137 "tmp.zig:15:9: note: referenced here",
89 });138 });
90139
...@@ -102,17 +151,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -102,17 +151,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
102 \\ _ = @intToFloat(f32, a);151 \\ _ = @intToFloat(f32, a);
103 \\}152 \\}
104 \\export fn qux() void {153 \\export fn qux() void {
105 \\ var a: f32 = 2;154 \\ var a: u32 = 2;
106 \\ _ = @floatCast(comptime_float, a);155 \\ _ = @floatCast(f32, a);
107 \\}156 \\}
108 , &[_][]const u8{157 , &[_][]const u8{
109 "tmp.zig:3:36: error: expected type 'comptime_float', found 'f32'",158 "tmp.zig:3:36: error: unable to evaluate constant expression",
110 "tmp.zig:3:9: note: referenced here",159 "tmp.zig:3:9: note: referenced here",
111 "tmp.zig:7:21: error: expected integer type, found 'f32'",160 "tmp.zig:7:21: error: expected integer type, found 'f32'",
112 "tmp.zig:7:9: note: referenced here",161 "tmp.zig:7:9: note: referenced here",
113 "tmp.zig:11:26: error: expected int type, found 'f32'",162 "tmp.zig:11:26: error: expected int type, found 'f32'",
114 "tmp.zig:11:9: note: referenced here",163 "tmp.zig:11:9: note: referenced here",
115 "tmp.zig:15:36: error: expected type 'comptime_float', found 'f32'",164 "tmp.zig:15:25: error: expected float type, found 'u32'",
116 "tmp.zig:15:9: note: referenced here",165 "tmp.zig:15:9: note: referenced here",
117 });166 });
118167
...@@ -1013,7 +1062,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1013,7 +1062,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1013 \\ storev(&v[i], 42);1062 \\ storev(&v[i], 42);
1014 \\}1063 \\}
1015 \\1064 \\
1016 \\fn storev(ptr: var, val: i32) void {1065 \\fn storev(ptr: anytype, val: i32) void {
1017 \\ ptr.* = val;1066 \\ ptr.* = val;
1018 \\}1067 \\}
1019 , &[_][]const u8{1068 , &[_][]const u8{
...@@ -1028,7 +1077,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1028,7 +1077,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1028 \\ var x = loadv(&v[i]);1077 \\ var x = loadv(&v[i]);
1029 \\}1078 \\}
1030 \\1079 \\
1031 \\fn loadv(ptr: var) i32 {1080 \\fn loadv(ptr: anytype) i32 {
1032 \\ return ptr.*;1081 \\ return ptr.*;
1033 \\}1082 \\}
1034 , &[_][]const u8{1083 , &[_][]const u8{
...@@ -1136,13 +1185,26 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1136,13 +1185,26 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1136 "tmp.zig:2:15: error: @Type not available for 'TypeInfo.Struct'",1185 "tmp.zig:2:15: error: @Type not available for 'TypeInfo.Struct'",
1137 });1186 });
11381187
1188 cases.add("wrong type for argument tuple to @asyncCall",
1189 \\export fn entry1() void {
1190 \\ var frame: @Frame(foo) = undefined;
1191 \\ @asyncCall(&frame, {}, foo, {});
1192 \\}
1193 \\
1194 \\fn foo() i32 {
1195 \\ return 0;
1196 \\}
1197 , &[_][]const u8{
1198 "tmp.zig:3:33: error: expected tuple or struct, found 'void'",
1199 });
1200
1139 cases.add("wrong type for result ptr to @asyncCall",1201 cases.add("wrong type for result ptr to @asyncCall",
1140 \\export fn entry() void {1202 \\export fn entry() void {
1141 \\ _ = async amain();1203 \\ _ = async amain();
1142 \\}1204 \\}
1143 \\fn amain() i32 {1205 \\fn amain() i32 {
1144 \\ var frame: @Frame(foo) = undefined;1206 \\ var frame: @Frame(foo) = undefined;
1145 \\ return await @asyncCall(&frame, false, foo);1207 \\ return await @asyncCall(&frame, false, foo, .{});
1146 \\}1208 \\}
1147 \\fn foo() i32 {1209 \\fn foo() i32 {
1148 \\ return 1234;1210 \\ return 1234;
...@@ -1283,7 +1345,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1283,7 +1345,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1283 \\export fn entry() void {1345 \\export fn entry() void {
1284 \\ var ptr: fn () callconv(.Async) void = func;1346 \\ var ptr: fn () callconv(.Async) void = func;
1285 \\ var bytes: [64]u8 = undefined;1347 \\ var bytes: [64]u8 = undefined;
1286 \\ _ = @asyncCall(&bytes, {}, ptr);1348 \\ _ = @asyncCall(&bytes, {}, ptr, .{});
1287 \\}1349 \\}
1288 \\fn func() callconv(.Async) void {}1350 \\fn func() callconv(.Async) void {}
1289 , &[_][]const u8{1351 , &[_][]const u8{
...@@ -1459,7 +1521,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1459,7 +1521,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1459 \\export fn entry() void {1521 \\export fn entry() void {
1460 \\ var ptr = afunc;1522 \\ var ptr = afunc;
1461 \\ var bytes: [100]u8 align(16) = undefined;1523 \\ var bytes: [100]u8 align(16) = undefined;
1462 \\ _ = @asyncCall(&bytes, {}, ptr);1524 \\ _ = @asyncCall(&bytes, {}, ptr, .{});
1463 \\}1525 \\}
1464 \\fn afunc() void { }1526 \\fn afunc() void { }
1465 , &[_][]const u8{1527 , &[_][]const u8{
...@@ -1798,7 +1860,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1798,7 +1860,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1798 \\ while (true) {}1860 \\ while (true) {}
1799 \\}1861 \\}
1800 , &[_][]const u8{1862 , &[_][]const u8{
1801 "error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn([]const u8,var) var'",1863 "error: expected type 'fn([]const u8, ?*std.builtin.StackTrace) noreturn', found 'fn([]const u8,anytype) anytype'",
1802 "note: only one of the functions is generic",1864 "note: only one of the functions is generic",
1803 });1865 });
18041866
...@@ -1998,11 +2060,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1998,11 +2060,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1998 });2060 });
19992061
2000 cases.add("export generic function",2062 cases.add("export generic function",
2001 \\export fn foo(num: var) i32 {2063 \\export fn foo(num: anytype) i32 {
2002 \\ return 0;2064 \\ return 0;
2003 \\}2065 \\}
2004 , &[_][]const u8{2066 , &[_][]const u8{
2005 "tmp.zig:1:15: error: parameter of type 'var' not allowed in function with calling convention 'C'",2067 "tmp.zig:1:15: error: parameter of type 'anytype' not allowed in function with calling convention 'C'",
2006 });2068 });
20072069
2008 cases.add("C pointer to c_void",2070 cases.add("C pointer to c_void",
...@@ -2802,7 +2864,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -2802,7 +2864,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2802 });2864 });
28032865
2804 cases.add("missing parameter name of generic function",2866 cases.add("missing parameter name of generic function",
2805 \\fn dump(var) void {}2867 \\fn dump(anytype) void {}
2806 \\export fn entry() void {2868 \\export fn entry() void {
2807 \\ var a: u8 = 9;2869 \\ var a: u8 = 9;
2808 \\ dump(a);2870 \\ dump(a);
...@@ -2825,13 +2887,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -2825,13 +2887,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2825 });2887 });
28262888
2827 cases.add("generic fn as parameter without comptime keyword",2889 cases.add("generic fn as parameter without comptime keyword",
2828 \\fn f(_: fn (var) void) void {}2890 \\fn f(_: fn (anytype) void) void {}
2829 \\fn g(_: var) void {}2891 \\fn g(_: anytype) void {}
2830 \\export fn entry() void {2892 \\export fn entry() void {
2831 \\ f(g);2893 \\ f(g);
2832 \\}2894 \\}
2833 , &[_][]const u8{2895 , &[_][]const u8{
2834 "tmp.zig:1:9: error: parameter of type 'fn(var) var' must be declared comptime",2896 "tmp.zig:1:9: error: parameter of type 'fn(anytype) anytype' must be declared comptime",
2835 });2897 });
28362898
2837 cases.add("optional pointer to void in extern struct",2899 cases.add("optional pointer to void in extern struct",
...@@ -3131,7 +3193,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -3131,7 +3193,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
31313193
3132 cases.add("var makes structs required to be comptime known",3194 cases.add("var makes structs required to be comptime known",
3133 \\export fn entry() void {3195 \\export fn entry() void {
3134 \\ const S = struct{v: var};3196 \\ const S = struct{v: anytype};
3135 \\ var s = S{.v=@as(i32, 10)};3197 \\ var s = S{.v=@as(i32, 10)};
3136 \\}3198 \\}
3137 , &[_][]const u8{3199 , &[_][]const u8{
...@@ -4354,6 +4416,40 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -4354,6 +4416,40 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
4354 "tmp.zig:5:14: note: previous value is here",4416 "tmp.zig:5:14: note: previous value is here",
4355 });4417 });
43564418
4419 cases.add("switch expression - duplicate type",
4420 \\fn foo(comptime T: type, x: T) u8 {
4421 \\ return switch (T) {
4422 \\ u32 => 0,
4423 \\ u64 => 1,
4424 \\ u32 => 2,
4425 \\ else => 3,
4426 \\ };
4427 \\}
4428 \\export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); }
4429 , &[_][]const u8{
4430 "tmp.zig:5:9: error: duplicate switch value",
4431 "tmp.zig:3:9: note: previous value is here",
4432 });
4433
4434 cases.add("switch expression - duplicate type (struct alias)",
4435 \\const Test = struct {
4436 \\ bar: i32,
4437 \\};
4438 \\const Test2 = Test;
4439 \\fn foo(comptime T: type, x: T) u8 {
4440 \\ return switch (T) {
4441 \\ Test => 0,
4442 \\ u64 => 1,
4443 \\ Test2 => 2,
4444 \\ else => 3,
4445 \\ };
4446 \\}
4447 \\export fn entry() usize { return @sizeOf(@TypeOf(foo(u32, 0))); }
4448 , &[_][]const u8{
4449 "tmp.zig:9:9: error: duplicate switch value",
4450 "tmp.zig:7:9: note: previous value is here",
4451 });
4452
4357 cases.add("switch expression - switch on pointer type with no else",4453 cases.add("switch expression - switch on pointer type with no else",
4358 \\fn foo(x: *u8) void {4454 \\fn foo(x: *u8) void {
4359 \\ switch (x) {4455 \\ switch (x) {
...@@ -6004,10 +6100,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -6004,10 +6100,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
6004 });6100 });
60056101
6006 cases.add("calling a generic function only known at runtime",6102 cases.add("calling a generic function only known at runtime",
6007 \\var foos = [_]fn(var) void { foo1, foo2 };6103 \\var foos = [_]fn(anytype) void { foo1, foo2 };
6008 \\6104 \\
6009 \\fn foo1(arg: var) void {}6105 \\fn foo1(arg: anytype) void {}
6010 \\fn foo2(arg: var) void {}6106 \\fn foo2(arg: anytype) void {}
6011 \\6107 \\
6012 \\pub fn main() !void {6108 \\pub fn main() !void {
6013 \\ foos[0](true);6109 \\ foos[0](true);
...@@ -6852,12 +6948,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -6852,12 +6948,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
6852 });6948 });
68536949
6854 cases.add("getting return type of generic function",6950 cases.add("getting return type of generic function",
6855 \\fn generic(a: var) void {}6951 \\fn generic(a: anytype) void {}
6856 \\comptime {6952 \\comptime {
6857 \\ _ = @TypeOf(generic).ReturnType;6953 \\ _ = @TypeOf(generic).ReturnType;
6858 \\}6954 \\}
6859 , &[_][]const u8{6955 , &[_][]const u8{
6860 "tmp.zig:3:25: error: ReturnType has not been resolved because 'fn(var) var' is generic",6956 "tmp.zig:3:25: error: ReturnType has not been resolved because 'fn(anytype) anytype' is generic",
6861 });6957 });
68626958
6863 cases.add("unsupported modifier at start of asm output constraint",6959 cases.add("unsupported modifier at start of asm output constraint",
...@@ -7425,7 +7521,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -7425,7 +7521,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
7425 });7521 });
74267522
7427 cases.add("issue #5221: invalid struct init type referenced by @typeInfo and passed into function",7523 cases.add("issue #5221: invalid struct init type referenced by @typeInfo and passed into function",
7428 \\fn ignore(comptime param: var) void {}7524 \\fn ignore(comptime param: anytype) void {}
7429 \\7525 \\
7430 \\export fn foo() void {7526 \\export fn foo() void {
7431 \\ const MyStruct = struct {7527 \\ const MyStruct = struct {
...@@ -7522,4 +7618,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -7522,4 +7618,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
7522 , &[_][]const u8{7618 , &[_][]const u8{
7523 "tmp.zig:2:9: error: @wasmMemoryGrow is a wasm32 feature only",7619 "tmp.zig:2:9: error: @wasmMemoryGrow is a wasm32 feature only",
7524 });7620 });
7621
7622 cases.add("Issue #5586: Make unary minus for unsigned types a compile error",
7623 \\export fn f(x: u32) u32 {
7624 \\ const y = -%x;
7625 \\ return -y;
7626 \\}
7627 , &[_][]const u8{
7628 "tmp.zig:3:12: error: negation of type 'u32'",
7629 });
7630
7631 cases.add("Issue #5618: coercion of ?*c_void to *c_void must fail.",
7632 \\export fn foo() void {
7633 \\ var u: ?*c_void = null;
7634 \\ var v: *c_void = undefined;
7635 \\ v = u;
7636 \\}
7637 , &[_][]const u8{
7638 "tmp.zig:4:9: error: expected type '*c_void', found '?*c_void'",
7639 });
7525}7640}
test/run_translated_c.zig+73-1
...@@ -268,5 +268,77 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -268,5 +268,77 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
268 \\ if (count != 4) abort();268 \\ if (count != 4) abort();
269 \\ return 0;269 \\ return 0;
270 \\}270 \\}
271 ,"");271 , "");
272
273 cases.add("array value type casts properly",
274 \\#include <stdlib.h>
275 \\unsigned int choose[53][10];
276 \\static int hash_binary(int k)
277 \\{
278 \\ choose[0][k] = 3;
279 \\ int sum = 0;
280 \\ sum += choose[0][k];
281 \\ return sum;
282 \\}
283 \\
284 \\int main() {
285 \\ int s = hash_binary(4);
286 \\ if (s != 3) abort();
287 \\ return 0;
288 \\}
289 , "");
290
291 cases.add("array value type casts properly use +=",
292 \\#include <stdlib.h>
293 \\static int hash_binary(int k)
294 \\{
295 \\ unsigned int choose[1][1] = {{3}};
296 \\ int sum = -1;
297 \\ int prev = 0;
298 \\ prev = sum += choose[0][0];
299 \\ if (sum != 2) abort();
300 \\ return sum + prev;
301 \\}
302 \\
303 \\int main() {
304 \\ int x = hash_binary(4);
305 \\ if (x != 4) abort();
306 \\ return 0;
307 \\}
308 , "");
309
310 cases.add("ensure array casts outisde +=",
311 \\#include <stdlib.h>
312 \\static int hash_binary(int k)
313 \\{
314 \\ unsigned int choose[3] = {1, 2, 3};
315 \\ int sum = -2;
316 \\ int prev = sum + choose[k];
317 \\ if (prev != 0) abort();
318 \\ return sum + prev;
319 \\}
320 \\
321 \\int main() {
322 \\ int x = hash_binary(1);
323 \\ if (x != -2) abort();
324 \\ return 0;
325 \\}
326 , "");
327
328 cases.add("array cast int to uint",
329 \\#include <stdlib.h>
330 \\static unsigned int hash_binary(int k)
331 \\{
332 \\ int choose[3] = {-1, -2, 3};
333 \\ unsigned int sum = 2;
334 \\ sum += choose[k];
335 \\ return sum;
336 \\}
337 \\
338 \\int main() {
339 \\ unsigned int x = hash_binary(1);
340 \\ if (x != 0) abort();
341 \\ return 0;
342 \\}
343 , "");
272}344}
test/runtime_safety.zig+11-1
...@@ -280,7 +280,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -280,7 +280,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
280 \\pub fn main() void {280 \\pub fn main() void {
281 \\ var bytes: [1]u8 align(16) = undefined;281 \\ var bytes: [1]u8 align(16) = undefined;
282 \\ var ptr = other;282 \\ var ptr = other;
283 \\ var frame = @asyncCall(&bytes, {}, ptr);283 \\ var frame = @asyncCall(&bytes, {}, ptr, .{});
284 \\}284 \\}
285 \\fn other() callconv(.Async) void {285 \\fn other() callconv(.Async) void {
286 \\ suspend;286 \\ suspend;
...@@ -757,6 +757,16 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -757,6 +757,16 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
757 \\}757 \\}
758 );758 );
759759
760 cases.addRuntimeSafety("unsigned integer not fitting in cast to signed integer - same bit count",
761 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
762 \\ @import("std").os.exit(126);
763 \\}
764 \\pub fn main() void {
765 \\ var value: u8 = 245;
766 \\ var casted = @intCast(i8, value);
767 \\}
768 );
769
760 cases.addRuntimeSafety("unwrap error",770 cases.addRuntimeSafety("unwrap error",
761 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {771 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
762 \\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) {772 \\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) {
test/stage1/behavior.zig+3
...@@ -50,6 +50,7 @@ comptime {...@@ -50,6 +50,7 @@ comptime {
50 _ = @import("behavior/bugs/4769_b.zig");50 _ = @import("behavior/bugs/4769_b.zig");
51 _ = @import("behavior/bugs/4769_c.zig");51 _ = @import("behavior/bugs/4769_c.zig");
52 _ = @import("behavior/bugs/4954.zig");52 _ = @import("behavior/bugs/4954.zig");
53 _ = @import("behavior/bugs/5413.zig");
53 _ = @import("behavior/bugs/5474.zig");54 _ = @import("behavior/bugs/5474.zig");
54 _ = @import("behavior/bugs/5487.zig");55 _ = @import("behavior/bugs/5487.zig");
55 _ = @import("behavior/bugs/394.zig");56 _ = @import("behavior/bugs/394.zig");
...@@ -131,4 +132,6 @@ comptime {...@@ -131,4 +132,6 @@ comptime {
131 }132 }
132 _ = @import("behavior/while.zig");133 _ = @import("behavior/while.zig");
133 _ = @import("behavior/widening.zig");134 _ = @import("behavior/widening.zig");
135 _ = @import("behavior/src.zig");
136 _ = @import("behavior/translate_c_macros.zig");
134}137}
test/stage1/behavior/async_fn.zig+18-18
...@@ -282,7 +282,7 @@ test "async fn pointer in a struct field" {...@@ -282,7 +282,7 @@ test "async fn pointer in a struct field" {
282 };282 };
283 var foo = Foo{ .bar = simpleAsyncFn2 };283 var foo = Foo{ .bar = simpleAsyncFn2 };
284 var bytes: [64]u8 align(16) = undefined;284 var bytes: [64]u8 align(16) = undefined;
285 const f = @asyncCall(&bytes, {}, foo.bar, &data);285 const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
286 comptime expect(@TypeOf(f) == anyframe->void);286 comptime expect(@TypeOf(f) == anyframe->void);
287 expect(data == 2);287 expect(data == 2);
288 resume f;288 resume f;
...@@ -318,7 +318,7 @@ test "@asyncCall with return type" {...@@ -318,7 +318,7 @@ test "@asyncCall with return type" {
318 var foo = Foo{ .bar = Foo.middle };318 var foo = Foo{ .bar = Foo.middle };
319 var bytes: [150]u8 align(16) = undefined;319 var bytes: [150]u8 align(16) = undefined;
320 var aresult: i32 = 0;320 var aresult: i32 = 0;
321 _ = @asyncCall(&bytes, &aresult, foo.bar);321 _ = @asyncCall(&bytes, &aresult, foo.bar, .{});
322 expect(aresult == 0);322 expect(aresult == 0);
323 resume Foo.global_frame;323 resume Foo.global_frame;
324 expect(aresult == 1234);324 expect(aresult == 1234);
...@@ -332,7 +332,7 @@ test "async fn with inferred error set" {...@@ -332,7 +332,7 @@ test "async fn with inferred error set" {
332 var frame: [1]@Frame(middle) = undefined;332 var frame: [1]@Frame(middle) = undefined;
333 var fn_ptr = middle;333 var fn_ptr = middle;
334 var result: @TypeOf(fn_ptr).ReturnType.ErrorSet!void = undefined;334 var result: @TypeOf(fn_ptr).ReturnType.ErrorSet!void = undefined;
335 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, fn_ptr);335 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, fn_ptr, .{});
336 resume global_frame;336 resume global_frame;
337 std.testing.expectError(error.Fail, result);337 std.testing.expectError(error.Fail, result);
338 }338 }
...@@ -827,7 +827,7 @@ test "cast fn to async fn when it is inferred to be async" {...@@ -827,7 +827,7 @@ test "cast fn to async fn when it is inferred to be async" {
827 ptr = func;827 ptr = func;
828 var buf: [100]u8 align(16) = undefined;828 var buf: [100]u8 align(16) = undefined;
829 var result: i32 = undefined;829 var result: i32 = undefined;
830 const f = @asyncCall(&buf, &result, ptr);830 const f = @asyncCall(&buf, &result, ptr, .{});
831 _ = await f;831 _ = await f;
832 expect(result == 1234);832 expect(result == 1234);
833 ok = true;833 ok = true;
...@@ -855,7 +855,7 @@ test "cast fn to async fn when it is inferred to be async, awaited directly" {...@@ -855,7 +855,7 @@ test "cast fn to async fn when it is inferred to be async, awaited directly" {
855 ptr = func;855 ptr = func;
856 var buf: [100]u8 align(16) = undefined;856 var buf: [100]u8 align(16) = undefined;
857 var result: i32 = undefined;857 var result: i32 = undefined;
858 _ = await @asyncCall(&buf, &result, ptr);858 _ = await @asyncCall(&buf, &result, ptr, .{});
859 expect(result == 1234);859 expect(result == 1234);
860 ok = true;860 ok = true;
861 }861 }
...@@ -951,7 +951,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" {...@@ -951,7 +951,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" {
951 fn doTheTest() void {951 fn doTheTest() void {
952 var frame: [1]@Frame(middle) = undefined;952 var frame: [1]@Frame(middle) = undefined;
953 var result: @TypeOf(middle).ReturnType.ErrorSet!void = undefined;953 var result: @TypeOf(middle).ReturnType.ErrorSet!void = undefined;
954 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, middle);954 _ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, middle, .{});
955 resume global_frame;955 resume global_frame;
956 std.testing.expectError(error.Fail, result);956 std.testing.expectError(error.Fail, result);
957 }957 }
...@@ -982,7 +982,7 @@ test "@asyncCall with actual frame instead of byte buffer" {...@@ -982,7 +982,7 @@ test "@asyncCall with actual frame instead of byte buffer" {
982 };982 };
983 var frame: @Frame(S.func) = undefined;983 var frame: @Frame(S.func) = undefined;
984 var result: i32 = undefined;984 var result: i32 = undefined;
985 const ptr = @asyncCall(&frame, &result, S.func);985 const ptr = @asyncCall(&frame, &result, S.func, .{});
986 resume ptr;986 resume ptr;
987 expect(result == 1234);987 expect(result == 1234);
988}988}
...@@ -1005,7 +1005,7 @@ test "@asyncCall using the result location inside the frame" {...@@ -1005,7 +1005,7 @@ test "@asyncCall using the result location inside the frame" {
1005 };1005 };
1006 var foo = Foo{ .bar = S.simple2 };1006 var foo = Foo{ .bar = S.simple2 };
1007 var bytes: [64]u8 align(16) = undefined;1007 var bytes: [64]u8 align(16) = undefined;
1008 const f = @asyncCall(&bytes, {}, foo.bar, &data);1008 const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
1009 comptime expect(@TypeOf(f) == anyframe->i32);1009 comptime expect(@TypeOf(f) == anyframe->i32);
1010 expect(data == 2);1010 expect(data == 2);
1011 resume f;1011 resume f;
...@@ -1016,7 +1016,7 @@ test "@asyncCall using the result location inside the frame" {...@@ -1016,7 +1016,7 @@ test "@asyncCall using the result location inside the frame" {
10161016
1017test "@TypeOf an async function call of generic fn with error union type" {1017test "@TypeOf an async function call of generic fn with error union type" {
1018 const S = struct {1018 const S = struct {
1019 fn func(comptime x: var) anyerror!i32 {1019 fn func(comptime x: anytype) anyerror!i32 {
1020 const T = @TypeOf(async func(x));1020 const T = @TypeOf(async func(x));
1021 comptime expect(T == @TypeOf(@frame()).Child);1021 comptime expect(T == @TypeOf(@frame()).Child);
1022 return undefined;1022 return undefined;
...@@ -1032,7 +1032,7 @@ test "using @TypeOf on a generic function call" {...@@ -1032,7 +1032,7 @@ test "using @TypeOf on a generic function call" {
10321032
1033 var buf: [100]u8 align(16) = undefined;1033 var buf: [100]u8 align(16) = undefined;
10341034
1035 fn amain(x: var) void {1035 fn amain(x: anytype) void {
1036 if (x == 0) {1036 if (x == 0) {
1037 global_ok = true;1037 global_ok = true;
1038 return;1038 return;
...@@ -1042,7 +1042,7 @@ test "using @TypeOf on a generic function call" {...@@ -1042,7 +1042,7 @@ test "using @TypeOf on a generic function call" {
1042 }1042 }
1043 const F = @TypeOf(async amain(x - 1));1043 const F = @TypeOf(async amain(x - 1));
1044 const frame = @intToPtr(*F, @ptrToInt(&buf));1044 const frame = @intToPtr(*F, @ptrToInt(&buf));
1045 return await @asyncCall(frame, {}, amain, x - 1);1045 return await @asyncCall(frame, {}, amain, .{x - 1});
1046 }1046 }
1047 };1047 };
1048 _ = async S.amain(@as(u32, 1));1048 _ = async S.amain(@as(u32, 1));
...@@ -1057,7 +1057,7 @@ test "recursive call of await @asyncCall with struct return type" {...@@ -1057,7 +1057,7 @@ test "recursive call of await @asyncCall with struct return type" {
10571057
1058 var buf: [100]u8 align(16) = undefined;1058 var buf: [100]u8 align(16) = undefined;
10591059
1060 fn amain(x: var) Foo {1060 fn amain(x: anytype) Foo {
1061 if (x == 0) {1061 if (x == 0) {
1062 global_ok = true;1062 global_ok = true;
1063 return Foo{ .x = 1, .y = 2, .z = 3 };1063 return Foo{ .x = 1, .y = 2, .z = 3 };
...@@ -1067,7 +1067,7 @@ test "recursive call of await @asyncCall with struct return type" {...@@ -1067,7 +1067,7 @@ test "recursive call of await @asyncCall with struct return type" {
1067 }1067 }
1068 const F = @TypeOf(async amain(x - 1));1068 const F = @TypeOf(async amain(x - 1));
1069 const frame = @intToPtr(*F, @ptrToInt(&buf));1069 const frame = @intToPtr(*F, @ptrToInt(&buf));
1070 return await @asyncCall(frame, {}, amain, x - 1);1070 return await @asyncCall(frame, {}, amain, .{x - 1});
1071 }1071 }
10721072
1073 const Foo = struct {1073 const Foo = struct {
...@@ -1078,7 +1078,7 @@ test "recursive call of await @asyncCall with struct return type" {...@@ -1078,7 +1078,7 @@ test "recursive call of await @asyncCall with struct return type" {
1078 };1078 };
1079 var res: S.Foo = undefined;1079 var res: S.Foo = undefined;
1080 var frame: @TypeOf(async S.amain(@as(u32, 1))) = undefined;1080 var frame: @TypeOf(async S.amain(@as(u32, 1))) = undefined;
1081 _ = @asyncCall(&frame, &res, S.amain, @as(u32, 1));1081 _ = @asyncCall(&frame, &res, S.amain, .{@as(u32, 1)});
1082 resume S.global_frame;1082 resume S.global_frame;
1083 expect(S.global_ok);1083 expect(S.global_ok);
1084 expect(res.x == 1);1084 expect(res.x == 1);
...@@ -1336,7 +1336,7 @@ test "async function passed 0-bit arg after non-0-bit arg" {...@@ -1336,7 +1336,7 @@ test "async function passed 0-bit arg after non-0-bit arg" {
1336 bar(1, .{}) catch unreachable;1336 bar(1, .{}) catch unreachable;
1337 }1337 }
13381338
1339 fn bar(x: i32, args: var) anyerror!void {1339 fn bar(x: i32, args: anytype) anyerror!void {
1340 global_frame = @frame();1340 global_frame = @frame();
1341 suspend;1341 suspend;
1342 global_int = x;1342 global_int = x;
...@@ -1357,7 +1357,7 @@ test "async function passed align(16) arg after align(8) arg" {...@@ -1357,7 +1357,7 @@ test "async function passed align(16) arg after align(8) arg" {
1357 bar(10, .{a}) catch unreachable;1357 bar(10, .{a}) catch unreachable;
1358 }1358 }
13591359
1360 fn bar(x: u64, args: var) anyerror!void {1360 fn bar(x: u64, args: anytype) anyerror!void {
1361 expect(x == 10);1361 expect(x == 10);
1362 global_frame = @frame();1362 global_frame = @frame();
1363 suspend;1363 suspend;
...@@ -1377,7 +1377,7 @@ test "async function call resolves target fn frame, comptime func" {...@@ -1377,7 +1377,7 @@ test "async function call resolves target fn frame, comptime func" {
1377 fn foo() anyerror!void {1377 fn foo() anyerror!void {
1378 const stack_size = 1000;1378 const stack_size = 1000;
1379 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;1379 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
1380 return await @asyncCall(&stack_frame, {}, bar);1380 return await @asyncCall(&stack_frame, {}, bar, .{});
1381 }1381 }
13821382
1383 fn bar() anyerror!void {1383 fn bar() anyerror!void {
...@@ -1400,7 +1400,7 @@ test "async function call resolves target fn frame, runtime func" {...@@ -1400,7 +1400,7 @@ test "async function call resolves target fn frame, runtime func" {
1400 const stack_size = 1000;1400 const stack_size = 1000;
1401 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;1401 var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
1402 var func: fn () callconv(.Async) anyerror!void = bar;1402 var func: fn () callconv(.Async) anyerror!void = bar;
1403 return await @asyncCall(&stack_frame, {}, func);1403 return await @asyncCall(&stack_frame, {}, func, .{});
1404 }1404 }
14051405
1406 fn bar() anyerror!void {1406 fn bar() anyerror!void {
test/stage1/behavior/bitcast.zig+2-2
...@@ -171,7 +171,7 @@ test "nested bitcast" {...@@ -171,7 +171,7 @@ test "nested bitcast" {
171171
172test "bitcast passed as tuple element" {172test "bitcast passed as tuple element" {
173 const S = struct {173 const S = struct {
174 fn foo(args: var) void {174 fn foo(args: anytype) void {
175 comptime expect(@TypeOf(args[0]) == f32);175 comptime expect(@TypeOf(args[0]) == f32);
176 expect(args[0] == 12.34);176 expect(args[0] == 12.34);
177 }177 }
...@@ -181,7 +181,7 @@ test "bitcast passed as tuple element" {...@@ -181,7 +181,7 @@ test "bitcast passed as tuple element" {
181181
182test "triple level result location with bitcast sandwich passed as tuple element" {182test "triple level result location with bitcast sandwich passed as tuple element" {
183 const S = struct {183 const S = struct {
184 fn foo(args: var) void {184 fn foo(args: anytype) void {
185 comptime expect(@TypeOf(args[0]) == f64);185 comptime expect(@TypeOf(args[0]) == f64);
186 expect(args[0] > 12.33 and args[0] < 12.35);186 expect(args[0] > 12.33 and args[0] < 12.35);
187 }187 }
test/stage1/behavior/bugs/2114.zig+1-1
...@@ -2,7 +2,7 @@ const std = @import("std");...@@ -2,7 +2,7 @@ const std = @import("std");
2const expect = std.testing.expect;2const expect = std.testing.expect;
3const math = std.math;3const math = std.math;
44
5fn ctz(x: var) usize {5fn ctz(x: anytype) usize {
6 return @ctz(@TypeOf(x), x);6 return @ctz(@TypeOf(x), x);
7}7}
88
test/stage1/behavior/bugs/3742.zig+1-1
...@@ -23,7 +23,7 @@ pub fn isCommand(comptime T: type) bool {...@@ -23,7 +23,7 @@ pub fn isCommand(comptime T: type) bool {
23}23}
2424
25pub const ArgSerializer = struct {25pub const ArgSerializer = struct {
26 pub fn serializeCommand(command: var) void {26 pub fn serializeCommand(command: anytype) void {
27 const CmdT = @TypeOf(command);27 const CmdT = @TypeOf(command);
2828
29 if (comptime isCommand(CmdT)) {29 if (comptime isCommand(CmdT)) {
test/stage1/behavior/bugs/4328.zig+4-4
...@@ -17,11 +17,11 @@ const S = extern struct {...@@ -17,11 +17,11 @@ const S = extern struct {
1717
18test "Extern function calls in @TypeOf" {18test "Extern function calls in @TypeOf" {
19 const Test = struct {19 const Test = struct {
20 fn test_fn_1(a: var, b: var) @TypeOf(printf("%d %s\n", a, b)) {20 fn test_fn_1(a: anytype, b: anytype) @TypeOf(printf("%d %s\n", a, b)) {
21 return 0;21 return 0;
22 }22 }
2323
24 fn test_fn_2(a: var) @TypeOf((S{ .state = 0 }).s_do_thing(a)) {24 fn test_fn_2(a: anytype) @TypeOf((S{ .state = 0 }).s_do_thing(a)) {
25 return 1;25 return 1;
26 }26 }
2727
...@@ -56,7 +56,7 @@ test "Extern function calls, dereferences and field access in @TypeOf" {...@@ -56,7 +56,7 @@ test "Extern function calls, dereferences and field access in @TypeOf" {
56 return .{ .dummy_field = 0 };56 return .{ .dummy_field = 0 };
57 }57 }
5858
59 fn test_fn_2(a: var) @TypeOf(fopen("test", "r").*.dummy_field) {59 fn test_fn_2(a: anytype) @TypeOf(fopen("test", "r").*.dummy_field) {
60 return 255;60 return 255;
61 }61 }
6262
...@@ -68,4 +68,4 @@ test "Extern function calls, dereferences and field access in @TypeOf" {...@@ -68,4 +68,4 @@ test "Extern function calls, dereferences and field access in @TypeOf" {
6868
69 Test.doTheTest();69 Test.doTheTest();
70 comptime Test.doTheTest();70 comptime Test.doTheTest();
71}
\ No newline at end of file
71}
test/stage1/behavior/bugs/5413.zig created+6
...@@ -0,0 +1,6 @@
1const expect = @import("std").testing.expect;
2
3test "Peer type resolution with string literals and unknown length u8 pointers" {
4 expect(@TypeOf("", "a", @as([*:0]const u8, "")) == [*:0]const u8);
5 expect(@TypeOf(@as([*:0]const u8, "baz"), "foo", "bar") == [*:0]const u8);
6}
test/stage1/behavior/byval_arg_var.zig+2-2
...@@ -13,11 +13,11 @@ fn start() void {...@@ -13,11 +13,11 @@ fn start() void {
13 foo("string literal");13 foo("string literal");
14}14}
1515
16fn foo(x: var) void {16fn foo(x: anytype) void {
17 bar(x);17 bar(x);
18}18}
1919
20fn bar(x: var) void {20fn bar(x: anytype) void {
21 result = x;21 result = x;
22}22}
2323
test/stage1/behavior/call.zig+1-1
...@@ -57,7 +57,7 @@ test "tuple parameters" {...@@ -57,7 +57,7 @@ test "tuple parameters" {
5757
58test "comptime call with bound function as parameter" {58test "comptime call with bound function as parameter" {
59 const S = struct {59 const S = struct {
60 fn ReturnType(func: var) type {60 fn ReturnType(func: anytype) type {
61 return switch (@typeInfo(@TypeOf(func))) {61 return switch (@typeInfo(@TypeOf(func))) {
62 .BoundFn => |info| info,62 .BoundFn => |info| info,
63 else => unreachable,63 else => unreachable,
test/stage1/behavior/cast.zig+13
...@@ -384,6 +384,19 @@ test "@intCast i32 to u7" {...@@ -384,6 +384,19 @@ test "@intCast i32 to u7" {
384 expect(z == 0xff);384 expect(z == 0xff);
385}385}
386386
387test "@floatCast cast down" {
388 {
389 var double: f64 = 0.001534;
390 var single = @floatCast(f32, double);
391 expect(single == 0.001534);
392 }
393 {
394 const double: f64 = 0.001534;
395 const single = @floatCast(f32, double);
396 expect(single == 0.001534);
397 }
398}
399
387test "implicit cast undefined to optional" {400test "implicit cast undefined to optional" {
388 expect(MakeType(void).getNull() == null);401 expect(MakeType(void).getNull() == null);
389 expect(MakeType(void).getNonNull() != null);402 expect(MakeType(void).getNonNull() != null);
test/stage1/behavior/enum.zig+25-1
...@@ -208,7 +208,7 @@ test "@tagName non-exhaustive enum" {...@@ -208,7 +208,7 @@ test "@tagName non-exhaustive enum" {
208 comptime expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));208 comptime expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B"));
209}209}
210210
211fn testEnumTagNameBare(n: var) []const u8 {211fn testEnumTagNameBare(n: anytype) []const u8 {
212 return @tagName(n);212 return @tagName(n);
213}213}
214214
...@@ -1140,3 +1140,27 @@ test "tagName on enum literals" {...@@ -1140,3 +1140,27 @@ test "tagName on enum literals" {
1140 expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));1140 expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
1141 comptime expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));1141 comptime expect(mem.eql(u8, @tagName(.FooBar), "FooBar"));
1142}1142}
1143
1144test "method call on an enum" {
1145 const S = struct {
1146 const E = enum {
1147 one,
1148 two,
1149
1150 fn method(self: *E) bool {
1151 return self.* == .two;
1152 }
1153
1154 fn generic_method(self: *E, foo: anytype) bool {
1155 return self.* == .two and foo == bool;
1156 }
1157 };
1158 fn doTheTest() void {
1159 var e = E.two;
1160 expect(e.method());
1161 expect(e.generic_method(bool));
1162 }
1163 };
1164 S.doTheTest();
1165 comptime S.doTheTest();
1166}
test/stage1/behavior/error.zig+1-1
...@@ -227,7 +227,7 @@ test "error: Infer error set from literals" {...@@ -227,7 +227,7 @@ test "error: Infer error set from literals" {
227 _ = comptime intLiteral("n") catch |err| handleErrors(err);227 _ = comptime intLiteral("n") catch |err| handleErrors(err);
228}228}
229229
230fn handleErrors(err: var) noreturn {230fn handleErrors(err: anytype) noreturn {
231 switch (err) {231 switch (err) {
232 error.T => {},232 error.T => {},
233 }233 }
test/stage1/behavior/eval.zig+5-6
...@@ -670,10 +670,10 @@ fn loopNTimes(comptime n: usize) void {...@@ -670,10 +670,10 @@ fn loopNTimes(comptime n: usize) void {
670}670}
671671
672test "variable inside inline loop that has different types on different iterations" {672test "variable inside inline loop that has different types on different iterations" {
673 testVarInsideInlineLoop(.{true, @as(u32, 42)});673 testVarInsideInlineLoop(.{ true, @as(u32, 42) });
674}674}
675675
676fn testVarInsideInlineLoop(args: var) void {676fn testVarInsideInlineLoop(args: anytype) void {
677 comptime var i = 0;677 comptime var i = 0;
678 inline while (i < args.len) : (i += 1) {678 inline while (i < args.len) : (i += 1) {
679 const x = args[i];679 const x = args[i];
...@@ -758,7 +758,7 @@ test "comptime bitwise operators" {...@@ -758,7 +758,7 @@ test "comptime bitwise operators" {
758test "*align(1) u16 is the same as *align(1:0:2) u16" {758test "*align(1) u16 is the same as *align(1:0:2) u16" {
759 comptime {759 comptime {
760 expect(*align(1:0:2) u16 == *align(1) u16);760 expect(*align(1:0:2) u16 == *align(1) u16);
761 expect(*align(:0:2) u16 == *u16);761 expect(*align(2:0:2) u16 == *u16);
762 }762 }
763}763}
764764
...@@ -814,17 +814,16 @@ test "two comptime calls with array default initialized to undefined" {...@@ -814,17 +814,16 @@ test "two comptime calls with array default initialized to undefined" {
814 dynamic_linker: DynamicLinker = DynamicLinker{},814 dynamic_linker: DynamicLinker = DynamicLinker{},
815815
816 pub fn parse() void {816 pub fn parse() void {
817 var result: CrossTarget = .{ };817 var result: CrossTarget = .{};
818 result.getCpuArch();818 result.getCpuArch();
819 }819 }
820820
821 pub fn getCpuArch(self: CrossTarget) void { }821 pub fn getCpuArch(self: CrossTarget) void {}
822 };822 };
823823
824 const DynamicLinker = struct {824 const DynamicLinker = struct {
825 buffer: [255]u8 = undefined,825 buffer: [255]u8 = undefined,
826 };826 };
827
828 };827 };
829828
830 comptime {829 comptime {
test/stage1/behavior/fn.zig+3-3
...@@ -104,7 +104,7 @@ test "number literal as an argument" {...@@ -104,7 +104,7 @@ test "number literal as an argument" {
104 comptime numberLiteralArg(3);104 comptime numberLiteralArg(3);
105}105}
106106
107fn numberLiteralArg(a: var) void {107fn numberLiteralArg(a: anytype) void {
108 expect(a == 3);108 expect(a == 3);
109}109}
110110
...@@ -132,7 +132,7 @@ test "pass by non-copying value through var arg" {...@@ -132,7 +132,7 @@ test "pass by non-copying value through var arg" {
132 expect(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3);132 expect(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3);
133}133}
134134
135fn addPointCoordsVar(pt: var) i32 {135fn addPointCoordsVar(pt: anytype) i32 {
136 comptime expect(@TypeOf(pt) == Point);136 comptime expect(@TypeOf(pt) == Point);
137 return pt.x + pt.y;137 return pt.x + pt.y;
138}138}
...@@ -267,7 +267,7 @@ test "ability to give comptime types and non comptime types to same parameter" {...@@ -267,7 +267,7 @@ test "ability to give comptime types and non comptime types to same parameter" {
267 expect(foo(i32) == 20);267 expect(foo(i32) == 20);
268 }268 }
269269
270 fn foo(arg: var) i32 {270 fn foo(arg: anytype) i32 {
271 if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20;271 if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20;
272 return 9 + arg;272 return 9 + arg;
273 }273 }
test/stage1/behavior/generics.zig+4-4
...@@ -47,7 +47,7 @@ comptime {...@@ -47,7 +47,7 @@ comptime {
47 expect(max_f64(1.2, 3.4) == 3.4);47 expect(max_f64(1.2, 3.4) == 3.4);
48}48}
4949
50fn max_var(a: var, b: var) @TypeOf(a + b) {50fn max_var(a: anytype, b: anytype) @TypeOf(a + b) {
51 return if (a > b) a else b;51 return if (a > b) a else b;
52}52}
5353
...@@ -133,15 +133,15 @@ fn getFirstByte(comptime T: type, mem: []const T) u8 {...@@ -133,15 +133,15 @@ fn getFirstByte(comptime T: type, mem: []const T) u8 {
133 return getByte(@ptrCast(*const u8, &mem[0]));133 return getByte(@ptrCast(*const u8, &mem[0]));
134}134}
135135
136const foos = [_]fn (var) bool{136const foos = [_]fn (anytype) bool{
137 foo1,137 foo1,
138 foo2,138 foo2,
139};139};
140140
141fn foo1(arg: var) bool {141fn foo1(arg: anytype) bool {
142 return arg;142 return arg;
143}143}
144fn foo2(arg: var) bool {144fn foo2(arg: anytype) bool {
145 return !arg;145 return !arg;
146}146}
147147
test/stage1/behavior/math.zig+122
...@@ -634,6 +634,128 @@ fn testSqrt(comptime T: type, x: T) void {...@@ -634,6 +634,128 @@ fn testSqrt(comptime T: type, x: T) void {
634 expect(@sqrt(x * x) == x);634 expect(@sqrt(x * x) == x);
635}635}
636636
637test "@fabs" {
638 testFabs(f128, 12.0);
639 comptime testFabs(f128, 12.0);
640 testFabs(f64, 12.0);
641 comptime testFabs(f64, 12.0);
642 testFabs(f32, 12.0);
643 comptime testFabs(f32, 12.0);
644 testFabs(f16, 12.0);
645 comptime testFabs(f16, 12.0);
646
647 const x = 14.0;
648 const y = -x;
649 const z = @fabs(y);
650 comptime expectEqual(x, z);
651}
652
653fn testFabs(comptime T: type, x: T) void {
654 const y = -x;
655 const z = @fabs(y);
656 expectEqual(x, z);
657}
658
659test "@floor" {
660 // FIXME: Generates a floorl function call
661 // testFloor(f128, 12.0);
662 comptime testFloor(f128, 12.0);
663 testFloor(f64, 12.0);
664 comptime testFloor(f64, 12.0);
665 testFloor(f32, 12.0);
666 comptime testFloor(f32, 12.0);
667 testFloor(f16, 12.0);
668 comptime testFloor(f16, 12.0);
669
670 const x = 14.0;
671 const y = x + 0.7;
672 const z = @floor(y);
673 comptime expectEqual(x, z);
674}
675
676fn testFloor(comptime T: type, x: T) void {
677 const y = x + 0.6;
678 const z = @floor(y);
679 expectEqual(x, z);
680}
681
682test "@ceil" {
683 // FIXME: Generates a ceill function call
684 //testCeil(f128, 12.0);
685 comptime testCeil(f128, 12.0);
686 testCeil(f64, 12.0);
687 comptime testCeil(f64, 12.0);
688 testCeil(f32, 12.0);
689 comptime testCeil(f32, 12.0);
690 testCeil(f16, 12.0);
691 comptime testCeil(f16, 12.0);
692
693 const x = 14.0;
694 const y = x - 0.7;
695 const z = @ceil(y);
696 comptime expectEqual(x, z);
697}
698
699fn testCeil(comptime T: type, x: T) void {
700 const y = x - 0.8;
701 const z = @ceil(y);
702 expectEqual(x, z);
703}
704
705test "@trunc" {
706 // FIXME: Generates a truncl function call
707 //testTrunc(f128, 12.0);
708 comptime testTrunc(f128, 12.0);
709 testTrunc(f64, 12.0);
710 comptime testTrunc(f64, 12.0);
711 testTrunc(f32, 12.0);
712 comptime testTrunc(f32, 12.0);
713 testTrunc(f16, 12.0);
714 comptime testTrunc(f16, 12.0);
715
716 const x = 14.0;
717 const y = x + 0.7;
718 const z = @trunc(y);
719 comptime expectEqual(x, z);
720}
721
722fn testTrunc(comptime T: type, x: T) void {
723 {
724 const y = x + 0.8;
725 const z = @trunc(y);
726 expectEqual(x, z);
727 }
728
729 {
730 const y = -x - 0.8;
731 const z = @trunc(y);
732 expectEqual(-x, z);
733 }
734}
735
736test "@round" {
737 // FIXME: Generates a roundl function call
738 //testRound(f128, 12.0);
739 comptime testRound(f128, 12.0);
740 testRound(f64, 12.0);
741 comptime testRound(f64, 12.0);
742 testRound(f32, 12.0);
743 comptime testRound(f32, 12.0);
744 testRound(f16, 12.0);
745 comptime testRound(f16, 12.0);
746
747 const x = 14.0;
748 const y = x + 0.4;
749 const z = @round(y);
750 comptime expectEqual(x, z);
751}
752
753fn testRound(comptime T: type, x: T) void {
754 const y = x - 0.5;
755 const z = @round(y);
756 expectEqual(x, z);
757}
758
637test "comptime_int param and return" {759test "comptime_int param and return" {
638 const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);760 const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);
639 expect(a == 137114567242441932203689521744947848950);761 expect(a == 137114567242441932203689521744947848950);
test/stage1/behavior/misc.zig+7
...@@ -713,3 +713,10 @@ test "auto created variables have correct alignment" {...@@ -713,3 +713,10 @@ test "auto created variables have correct alignment" {
713 expect(S.foo("\x7a\x7a\x7a\x7a") == 0x7a7a7a7a);713 expect(S.foo("\x7a\x7a\x7a\x7a") == 0x7a7a7a7a);
714 comptime expect(S.foo("\x7a\x7a\x7a\x7a") == 0x7a7a7a7a);714 comptime expect(S.foo("\x7a\x7a\x7a\x7a") == 0x7a7a7a7a);
715}715}
716
717extern var opaque_extern_var: @Type(.Opaque);
718var var_to_export: u32 = 42;
719test "extern variable with non-pointer opaque type" {
720 @export(var_to_export, .{ .name = "opaque_extern_var" });
721 expect(@ptrCast(*align(1) u32, &opaque_extern_var).* == 42);
722}
test/stage1/behavior/optional.zig+14-2
...@@ -67,8 +67,20 @@ fn test_cmp_optional_non_optional() void {...@@ -67,8 +67,20 @@ fn test_cmp_optional_non_optional() void {
67 // test evaluation is always lexical67 // test evaluation is always lexical
68 // ensure that the optional isn't always computed before the non-optional68 // ensure that the optional isn't always computed before the non-optional
69 var mutable_state: i32 = 0;69 var mutable_state: i32 = 0;
70 _ = blk1: { mutable_state += 1; break :blk1 @as(?f64, 10.0); } != blk2: { expect(mutable_state == 1); break :blk2 @as(f64, 5.0); };70 _ = blk1: {
71 _ = blk1: { mutable_state += 1; break :blk1 @as(f64, 10.0); } != blk2: { expect(mutable_state == 2); break :blk2 @as(?f64, 5.0); };71 mutable_state += 1;
72 break :blk1 @as(?f64, 10.0);
73 } != blk2: {
74 expect(mutable_state == 1);
75 break :blk2 @as(f64, 5.0);
76 };
77 _ = blk1: {
78 mutable_state += 1;
79 break :blk1 @as(f64, 10.0);
80 } != blk2: {
81 expect(mutable_state == 2);
82 break :blk2 @as(?f64, 5.0);
83 };
72}84}
7385
74test "passing an optional integer as a parameter" {86test "passing an optional integer as a parameter" {
test/stage1/behavior/slice.zig+5
...@@ -280,6 +280,11 @@ test "slice syntax resulting in pointer-to-array" {...@@ -280,6 +280,11 @@ test "slice syntax resulting in pointer-to-array" {
280 expect(slice[0] == 5);280 expect(slice[0] == 5);
281 comptime expect(@TypeOf(src_slice[0..2]) == *align(4) [2]u8);281 comptime expect(@TypeOf(src_slice[0..2]) == *align(4) [2]u8);
282 }282 }
283
284 fn testConcatStrLiterals() void {
285 expectEqualSlices("a"[0..] ++ "b"[0..], "ab");
286 expectEqualSlices("a"[0..:0] ++ "b"[0..:0], "ab");
287 }
283 };288 };
284289
285 S.doTheTest();290 S.doTheTest();
test/stage1/behavior/src.zig created+17
...@@ -0,0 +1,17 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "@src" {
5 doTheTest();
6}
7
8fn doTheTest() void {
9 const src = @src();
10
11 expect(src.line == 9);
12 expect(src.column == 17);
13 expect(std.mem.endsWith(u8, src.fn_name, "doTheTest"));
14 expect(std.mem.endsWith(u8, src.file, "src.zig"));
15 expect(src.fn_name[src.fn_name.len] == 0);
16 expect(src.file[src.file.len] == 0);
17}
test/stage1/behavior/struct.zig+38-5
...@@ -713,7 +713,7 @@ test "packed struct field passed to generic function" {...@@ -713,7 +713,7 @@ test "packed struct field passed to generic function" {
713 a: u1,713 a: u1,
714 };714 };
715715
716 fn genericReadPackedField(ptr: var) u5 {716 fn genericReadPackedField(ptr: anytype) u5 {
717 return ptr.*;717 return ptr.*;
718 }718 }
719 };719 };
...@@ -754,7 +754,7 @@ test "fully anonymous struct" {...@@ -754,7 +754,7 @@ test "fully anonymous struct" {
754 .s = "hi",754 .s = "hi",
755 });755 });
756 }756 }
757 fn dump(args: var) void {757 fn dump(args: anytype) void {
758 expect(args.int == 1234);758 expect(args.int == 1234);
759 expect(args.float == 12.34);759 expect(args.float == 12.34);
760 expect(args.b);760 expect(args.b);
...@@ -771,7 +771,7 @@ test "fully anonymous list literal" {...@@ -771,7 +771,7 @@ test "fully anonymous list literal" {
771 fn doTheTest() void {771 fn doTheTest() void {
772 dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" });772 dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi" });
773 }773 }
774 fn dump(args: var) void {774 fn dump(args: anytype) void {
775 expect(args.@"0" == 1234);775 expect(args.@"0" == 1234);
776 expect(args.@"1" == 12.34);776 expect(args.@"1" == 12.34);
777 expect(args.@"2");777 expect(args.@"2");
...@@ -792,8 +792,8 @@ test "anonymous struct literal assigned to variable" {...@@ -792,8 +792,8 @@ test "anonymous struct literal assigned to variable" {
792792
793test "struct with var field" {793test "struct with var field" {
794 const Point = struct {794 const Point = struct {
795 x: var,795 x: anytype,
796 y: var,796 y: anytype,
797 };797 };
798 const pt = Point{798 const pt = Point{
799 .x = 1,799 .x = 1,
...@@ -851,3 +851,36 @@ test "struct with union field" {...@@ -851,3 +851,36 @@ test "struct with union field" {
851 expectEqual(@as(u32, 2), True.ref);851 expectEqual(@as(u32, 2), True.ref);
852 expectEqual(true, True.kind.Bool);852 expectEqual(true, True.kind.Bool);
853}853}
854
855test "type coercion of anon struct literal to struct" {
856 const S = struct {
857 const S2 = struct {
858 A: u32,
859 B: []const u8,
860 C: void,
861 D: Foo = .{},
862 };
863
864 const Foo = struct {
865 field: i32 = 1234,
866 };
867
868 fn doTheTest() void {
869 var y: u32 = 42;
870 const t0 = .{ .A = 123, .B = "foo", .C = {} };
871 const t1 = .{ .A = y, .B = "foo", .C = {} };
872 const y0: S2 = t0;
873 var y1: S2 = t1;
874 expect(y0.A == 123);
875 expect(std.mem.eql(u8, y0.B, "foo"));
876 expect(y0.C == {});
877 expect(y0.D.field == 1234);
878 expect(y1.A == y);
879 expect(std.mem.eql(u8, y1.B, "foo"));
880 expect(y1.C == {});
881 expect(y1.D.field == 1234);
882 }
883 };
884 S.doTheTest();
885 comptime S.doTheTest();
886}
test/stage1/behavior/translate_c_macros.h created+9
...@@ -0,0 +1,9 @@
1// initializer list expression
2typedef struct Color {
3 unsigned char r;
4 unsigned char g;
5 unsigned char b;
6 unsigned char a;
7} Color;
8#define CLITERAL(type) (type)
9#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
\ No newline at end of file
test/stage1/behavior/translate_c_macros.zig created+12
...@@ -0,0 +1,12 @@
1const expect = @import("std").testing.expect;
2
3const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h"));
4
5test "initializer list expression" {
6 @import("std").testing.expectEqual(h.Color{
7 .r = 200,
8 .g = 200,
9 .b = 200,
10 .a = 255,
11 }, h.LIGHTGRAY);
12}
test/stage1/behavior/tuple.zig+2-2
...@@ -42,7 +42,7 @@ test "tuple multiplication" {...@@ -42,7 +42,7 @@ test "tuple multiplication" {
42 comptime S.doTheTest();42 comptime S.doTheTest();
4343
44 const T = struct {44 const T = struct {
45 fn consume_tuple(tuple: var, len: usize) void {45 fn consume_tuple(tuple: anytype, len: usize) void {
46 expect(tuple.len == len);46 expect(tuple.len == len);
47 }47 }
4848
...@@ -82,7 +82,7 @@ test "tuple multiplication" {...@@ -82,7 +82,7 @@ test "tuple multiplication" {
8282
83test "pass tuple to comptime var parameter" {83test "pass tuple to comptime var parameter" {
84 const S = struct {84 const S = struct {
85 fn Foo(comptime args: var) void {85 fn Foo(comptime args: anytype) void {
86 expect(args[0] == 1);86 expect(args[0] == 1);
87 }87 }
8888
test/stage1/behavior/type.zig+23
...@@ -213,3 +213,26 @@ test "Type.AnyFrame" {...@@ -213,3 +213,26 @@ test "Type.AnyFrame" {
213 anyframe->anyframe->u8,213 anyframe->anyframe->u8,
214 });214 });
215}215}
216
217test "Type.EnumLiteral" {
218 testTypes(&[_]type{
219 @TypeOf(.Dummy),
220 });
221}
222
223fn add(a: i32, b: i32) i32 {
224 return a + b;
225}
226
227test "Type.Frame" {
228 testTypes(&[_]type{
229 @Frame(add),
230 });
231}
232
233test "Type.ErrorSet" {
234 // error sets don't compare equal so just check if they compile
235 _ = @Type(@typeInfo(error{}));
236 _ = @Type(@typeInfo(error{A}));
237 _ = @Type(@typeInfo(error{ A, B, C }));
238}
test/stage1/behavior/type_info.zig+46-5
...@@ -202,7 +202,7 @@ fn testUnion() void {...@@ -202,7 +202,7 @@ fn testUnion() void {
202 expect(typeinfo_info.Union.fields[4].enum_field != null);202 expect(typeinfo_info.Union.fields[4].enum_field != null);
203 expect(typeinfo_info.Union.fields[4].enum_field.?.value == 4);203 expect(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
204 expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int));204 expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int));
205 expect(typeinfo_info.Union.decls.len == 20);205 expect(typeinfo_info.Union.decls.len == 21);
206206
207 const TestNoTagUnion = union {207 const TestNoTagUnion = union {
208 Foo: void,208 Foo: void,
...@@ -251,14 +251,13 @@ fn testStruct() void {...@@ -251,14 +251,13 @@ fn testStruct() void {
251}251}
252252
253const TestStruct = packed struct {253const TestStruct = packed struct {
254 const Self = @This();
255
256 fieldA: usize,254 fieldA: usize,
257 fieldB: void,255 fieldB: void,
258 fieldC: *Self,256 fieldC: *Self,
259 fieldD: u32 = 4,257 fieldD: u32 = 4,
260258
261 pub fn foo(self: *const Self) void {}259 pub fn foo(self: *const Self) void {}
260 const Self = @This();
262};261};
263262
264test "type info: function type info" {263test "type info: function type info" {
...@@ -281,7 +280,7 @@ fn testFunction() void {...@@ -281,7 +280,7 @@ fn testFunction() void {
281 expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct);280 expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct);
282}281}
283282
284extern fn foo(a: usize, b: bool, args: ...) usize;283extern fn foo(a: usize, b: bool, ...) usize;
285284
286test "typeInfo with comptime parameter in struct fn def" {285test "typeInfo with comptime parameter in struct fn def" {
287 const S = struct {286 const S = struct {
...@@ -386,6 +385,48 @@ test "@typeInfo does not force declarations into existence" {...@@ -386,6 +385,48 @@ test "@typeInfo does not force declarations into existence" {
386}385}
387386
388test "defaut value for a var-typed field" {387test "defaut value for a var-typed field" {
389 const S = struct { x: var };388 const S = struct { x: anytype };
390 expect(@typeInfo(S).Struct.fields[0].default_value == null);389 expect(@typeInfo(S).Struct.fields[0].default_value == null);
391}390}
391
392fn add(a: i32, b: i32) i32 {
393 return a + b;
394}
395
396test "type info for async frames" {
397 switch (@typeInfo(@Frame(add))) {
398 .Frame => |frame| {
399 expect(frame.function == add);
400 },
401 else => unreachable,
402 }
403}
404
405test "type info: value is correctly copied" {
406 comptime {
407 var ptrInfo = @typeInfo([]u32);
408 ptrInfo.Pointer.size = .One;
409 expect(@typeInfo([]u32).Pointer.size == .Slice);
410 }
411}
412
413test "Declarations are returned in declaration order" {
414 const S = struct {
415 const a = 1;
416 const b = 2;
417 const c = 3;
418 const d = 4;
419 const e = 5;
420 };
421 const d = @typeInfo(S).Struct.decls;
422 expect(std.mem.eql(u8, d[0].name, "a"));
423 expect(std.mem.eql(u8, d[1].name, "b"));
424 expect(std.mem.eql(u8, d[2].name, "c"));
425 expect(std.mem.eql(u8, d[3].name, "d"));
426 expect(std.mem.eql(u8, d[4].name, "e"));
427}
428
429test "Struct.is_tuple" {
430 expect(@typeInfo(@TypeOf(.{0})).Struct.is_tuple);
431 expect(!@typeInfo(@TypeOf(.{ .a = 0 })).Struct.is_tuple);
432}
test/stage1/behavior/union.zig+22-1
...@@ -296,7 +296,7 @@ const TaggedUnionWithAVoid = union(enum) {...@@ -296,7 +296,7 @@ const TaggedUnionWithAVoid = union(enum) {
296 B: i32,296 B: i32,
297};297};
298298
299fn testTaggedUnionInit(x: var) bool {299fn testTaggedUnionInit(x: anytype) bool {
300 const y = TaggedUnionWithAVoid{ .A = x };300 const y = TaggedUnionWithAVoid{ .A = x };
301 return @as(@TagType(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A;301 return @as(@TagType(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A;
302}302}
...@@ -669,3 +669,24 @@ test "cast from anonymous struct to union" {...@@ -669,3 +669,24 @@ test "cast from anonymous struct to union" {
669 S.doTheTest();669 S.doTheTest();
670 comptime S.doTheTest();670 comptime S.doTheTest();
671}671}
672
673test "method call on an empty union" {
674 const S = struct {
675 const MyUnion = union(Tag) {
676 pub const Tag = enum { X1, X2 };
677 X1: [0]u8,
678 X2: [0]u8,
679
680 pub fn useIt(self: *@This()) bool {
681 return true;
682 }
683 };
684
685 fn doTheTest() void {
686 var u = MyUnion{ .X1 = [0]u8{} };
687 expect(u.useIt());
688 }
689 };
690 S.doTheTest();
691 comptime S.doTheTest();
692}
test/stage1/behavior/var_args.zig+8-8
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const expect = @import("std").testing.expect;1const expect = @import("std").testing.expect;
22
3fn add(args: var) i32 {3fn add(args: anytype) i32 {
4 var sum = @as(i32, 0);4 var sum = @as(i32, 0);
5 {5 {
6 comptime var i: usize = 0;6 comptime var i: usize = 0;
...@@ -17,7 +17,7 @@ test "add arbitrary args" {...@@ -17,7 +17,7 @@ test "add arbitrary args" {
17 expect(add(.{}) == 0);17 expect(add(.{}) == 0);
18}18}
1919
20fn readFirstVarArg(args: var) void {20fn readFirstVarArg(args: anytype) void {
21 const value = args[0];21 const value = args[0];
22}22}
2323
...@@ -31,7 +31,7 @@ test "pass args directly" {...@@ -31,7 +31,7 @@ test "pass args directly" {
31 expect(addSomeStuff(.{}) == 0);31 expect(addSomeStuff(.{}) == 0);
32}32}
3333
34fn addSomeStuff(args: var) i32 {34fn addSomeStuff(args: anytype) i32 {
35 return add(args);35 return add(args);
36}36}
3737
...@@ -47,7 +47,7 @@ test "runtime parameter before var args" {...@@ -47,7 +47,7 @@ test "runtime parameter before var args" {
47 }47 }
48}48}
4949
50fn extraFn(extra: u32, args: var) usize {50fn extraFn(extra: u32, args: anytype) usize {
51 if (args.len >= 1) {51 if (args.len >= 1) {
52 expect(args[0] == false);52 expect(args[0] == false);
53 }53 }
...@@ -57,15 +57,15 @@ fn extraFn(extra: u32, args: var) usize {...@@ -57,15 +57,15 @@ fn extraFn(extra: u32, args: var) usize {
57 return args.len;57 return args.len;
58}58}
5959
60const foos = [_]fn (var) bool{60const foos = [_]fn (anytype) bool{
61 foo1,61 foo1,
62 foo2,62 foo2,
63};63};
6464
65fn foo1(args: var) bool {65fn foo1(args: anytype) bool {
66 return true;66 return true;
67}67}
68fn foo2(args: var) bool {68fn foo2(args: anytype) bool {
69 return false;69 return false;
70}70}
7171
...@@ -78,6 +78,6 @@ test "pass zero length array to var args param" {...@@ -78,6 +78,6 @@ test "pass zero length array to var args param" {
78 doNothingWithFirstArg(.{""});78 doNothingWithFirstArg(.{""});
79}79}
8080
81fn doNothingWithFirstArg(args: var) void {81fn doNothingWithFirstArg(args: anytype) void {
82 const a = args[0];82 const a = args[0];
83}83}
test/stage1/behavior/vector.zig+4-4
...@@ -171,7 +171,7 @@ test "load vector elements via comptime index" {...@@ -171,7 +171,7 @@ test "load vector elements via comptime index" {
171 expect(v[1] == 2);171 expect(v[1] == 2);
172 expect(loadv(&v[2]) == 3);172 expect(loadv(&v[2]) == 3);
173 }173 }
174 fn loadv(ptr: var) i32 {174 fn loadv(ptr: anytype) i32 {
175 return ptr.*;175 return ptr.*;
176 }176 }
177 };177 };
...@@ -194,7 +194,7 @@ test "store vector elements via comptime index" {...@@ -194,7 +194,7 @@ test "store vector elements via comptime index" {
194 storev(&v[0], 100);194 storev(&v[0], 100);
195 expect(v[0] == 100);195 expect(v[0] == 100);
196 }196 }
197 fn storev(ptr: var, x: i32) void {197 fn storev(ptr: anytype, x: i32) void {
198 ptr.* = x;198 ptr.* = x;
199 }199 }
200 };200 };
...@@ -392,7 +392,7 @@ test "vector shift operators" {...@@ -392,7 +392,7 @@ test "vector shift operators" {
392 if (builtin.os.tag == .wasi) return error.SkipZigTest;392 if (builtin.os.tag == .wasi) return error.SkipZigTest;
393393
394 const S = struct {394 const S = struct {
395 fn doTheTestShift(x: var, y: var) void {395 fn doTheTestShift(x: anytype, y: anytype) void {
396 const N = @typeInfo(@TypeOf(x)).Array.len;396 const N = @typeInfo(@TypeOf(x)).Array.len;
397 const TX = @typeInfo(@TypeOf(x)).Array.child;397 const TX = @typeInfo(@TypeOf(x)).Array.child;
398 const TY = @typeInfo(@TypeOf(y)).Array.child;398 const TY = @typeInfo(@TypeOf(y)).Array.child;
...@@ -409,7 +409,7 @@ test "vector shift operators" {...@@ -409,7 +409,7 @@ test "vector shift operators" {
409 expectEqual(x[i] << y[i], v);409 expectEqual(x[i] << y[i], v);
410 }410 }
411 }411 }
412 fn doTheTestShiftExact(x: var, y: var, dir: enum { Left, Right }) void {412 fn doTheTestShiftExact(x: anytype, y: anytype, dir: enum { Left, Right }) void {
413 const N = @typeInfo(@TypeOf(x)).Array.len;413 const N = @typeInfo(@TypeOf(x)).Array.len;
414 const TX = @typeInfo(@TypeOf(x)).Array.child;414 const TX = @typeInfo(@TypeOf(x)).Array.child;
415 const TY = @typeInfo(@TypeOf(y)).Array.child;415 const TY = @typeInfo(@TypeOf(y)).Array.child;
test/stage2/cbe.zig created+91
...@@ -0,0 +1,91 @@
1const std = @import("std");
2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3
4// These tests should work with all platforms, but we're using linux_x64 for
5// now for consistency. Will be expanded eventually.
6const linux_x64 = std.zig.CrossTarget{
7 .cpu_arch = .x86_64,
8 .os_tag = .linux,
9};
10
11pub fn addCases(ctx: *TestContext) !void {
12 ctx.c("empty start function", linux_x64,
13 \\export fn _start() noreturn {}
14 ,
15 \\noreturn void _start(void) {}
16 \\
17 );
18 ctx.c("less empty start function", linux_x64,
19 \\fn main() noreturn {}
20 \\
21 \\export fn _start() noreturn {
22 \\ main();
23 \\}
24 ,
25 \\noreturn void main(void);
26 \\
27 \\noreturn void _start(void) {
28 \\ main();
29 \\}
30 \\
31 \\noreturn void main(void) {}
32 \\
33 );
34 // TODO: implement return values
35 // TODO: figure out a way to prevent asm constants from being generated
36 ctx.c("inline asm", linux_x64,
37 \\fn exitGood() void {
38 \\ asm volatile ("syscall"
39 \\ :
40 \\ : [number] "{rax}" (231),
41 \\ [arg1] "{rdi}" (0)
42 \\ );
43 \\}
44 \\
45 \\export fn _start() noreturn {
46 \\ exitGood();
47 \\}
48 ,
49 \\#include <stddef.h>
50 \\
51 \\void exitGood(void);
52 \\
53 \\const char *const exitGood__anon_0 = "{rax}";
54 \\const char *const exitGood__anon_1 = "{rdi}";
55 \\const char *const exitGood__anon_2 = "syscall";
56 \\
57 \\noreturn void _start(void) {
58 \\ exitGood();
59 \\}
60 \\
61 \\void exitGood(void) {
62 \\ register size_t rax_constant __asm__("rax") = 231;
63 \\ register size_t rdi_constant __asm__("rdi") = 0;
64 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
65 \\ return;
66 \\}
67 \\
68 );
69 ctx.c("basic return", linux_x64,
70 \\fn main() u8 {
71 \\ return 103;
72 \\}
73 \\
74 \\export fn _start() noreturn {
75 \\ _ = main();
76 \\}
77 ,
78 \\#include <stdint.h>
79 \\
80 \\uint8_t main(void);
81 \\
82 \\noreturn void _start(void) {
83 \\ (void)main();
84 \\}
85 \\
86 \\uint8_t main(void) {
87 \\ return 103;
88 \\}
89 \\
90 );
91}
test/stage2/compare_output.zig+223-21
...@@ -1,28 +1,230 @@...@@ -1,28 +1,230 @@
1const std = @import("std");1const std = @import("std");
2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3// self-hosted does not yet support PE executable files / COFF object files
4// or mach-o files. So we do these test cases cross compiling for x86_64-linux.
5const linux_x64 = std.zig.CrossTarget{
6 .cpu_arch = .x86_64,
7 .os_tag = .linux,
8};
39
4pub fn addCases(ctx: *TestContext) !void {10pub fn addCases(ctx: *TestContext) !void {
5 // TODO: re-enable these tests.11 if (std.Target.current.os.tag != .linux or
6 // https://github.com/ziglang/zig/issues/136412 std.Target.current.cpu.arch != .x86_64)
13 {
14 // TODO implement self-hosted PE (.exe file) linking
15 // TODO implement more ZIR so we don't depend on x86_64-linux
16 return;
17 }
718
8 //// hello world19 {
9 //try ctx.testCompareOutputLibC(20 var case = ctx.exe("hello world with updates", linux_x64);
10 // \\extern fn puts([*]const u8) void;21 // Regular old hello world
11 // \\pub export fn main() c_int {22 case.addCompareOutput(
12 // \\ puts("Hello, world!");23 \\export fn _start() noreturn {
13 // \\ return 0;24 \\ print();
14 // \\}25 \\
15 //, "Hello, world!" ++ std.cstr.line_sep);26 \\ exit();
27 \\}
28 \\
29 \\fn print() void {
30 \\ asm volatile ("syscall"
31 \\ :
32 \\ : [number] "{rax}" (1),
33 \\ [arg1] "{rdi}" (1),
34 \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
35 \\ [arg3] "{rdx}" (14)
36 \\ : "rcx", "r11", "memory"
37 \\ );
38 \\ return;
39 \\}
40 \\
41 \\fn exit() noreturn {
42 \\ asm volatile ("syscall"
43 \\ :
44 \\ : [number] "{rax}" (231),
45 \\ [arg1] "{rdi}" (0)
46 \\ : "rcx", "r11", "memory"
47 \\ );
48 \\ unreachable;
49 \\}
50 ,
51 "Hello, World!\n",
52 );
53 // Now change the message only
54 case.addCompareOutput(
55 \\export fn _start() noreturn {
56 \\ print();
57 \\
58 \\ exit();
59 \\}
60 \\
61 \\fn print() void {
62 \\ asm volatile ("syscall"
63 \\ :
64 \\ : [number] "{rax}" (1),
65 \\ [arg1] "{rdi}" (1),
66 \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
67 \\ [arg3] "{rdx}" (104)
68 \\ : "rcx", "r11", "memory"
69 \\ );
70 \\ return;
71 \\}
72 \\
73 \\fn exit() noreturn {
74 \\ asm volatile ("syscall"
75 \\ :
76 \\ : [number] "{rax}" (231),
77 \\ [arg1] "{rdi}" (0)
78 \\ : "rcx", "r11", "memory"
79 \\ );
80 \\ unreachable;
81 \\}
82 ,
83 "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
84 );
85 // Now we print it twice.
86 case.addCompareOutput(
87 \\export fn _start() noreturn {
88 \\ print();
89 \\ print();
90 \\
91 \\ exit();
92 \\}
93 \\
94 \\fn print() void {
95 \\ asm volatile ("syscall"
96 \\ :
97 \\ : [number] "{rax}" (1),
98 \\ [arg1] "{rdi}" (1),
99 \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
100 \\ [arg3] "{rdx}" (104)
101 \\ : "rcx", "r11", "memory"
102 \\ );
103 \\ return;
104 \\}
105 \\
106 \\fn exit() noreturn {
107 \\ asm volatile ("syscall"
108 \\ :
109 \\ : [number] "{rax}" (231),
110 \\ [arg1] "{rdi}" (0)
111 \\ : "rcx", "r11", "memory"
112 \\ );
113 \\ unreachable;
114 \\}
115 ,
116 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
117 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
118 \\
119 );
120 }
16121
17 //// function calling another function122 {
18 //try ctx.testCompareOutputLibC(123 var case = ctx.exe("adding numbers at comptime", linux_x64);
19 // \\extern fn puts(s: [*]const u8) void;124 case.addCompareOutput(
20 // \\pub export fn main() c_int {125 \\export fn _start() noreturn {
21 // \\ return foo("OK");126 \\ asm volatile ("syscall"
22 // \\}127 \\ :
23 // \\fn foo(s: [*]const u8) c_int {128 \\ : [number] "{rax}" (1),
24 // \\ puts(s);129 \\ [arg1] "{rdi}" (1),
25 // \\ return 0;130 \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
26 // \\}131 \\ [arg3] "{rdx}" (10 + 4)
27 //, "OK" ++ std.cstr.line_sep);132 \\ : "rcx", "r11", "memory"
133 \\ );
134 \\ asm volatile ("syscall"
135 \\ :
136 \\ : [number] "{rax}" (@as(usize, 230) + @as(usize, 1)),
137 \\ [arg1] "{rdi}" (0)
138 \\ : "rcx", "r11", "memory"
139 \\ );
140 \\ unreachable;
141 \\}
142 ,
143 "Hello, World!\n",
144 );
145 }
146
147 {
148 var case = ctx.exe("adding numbers at runtime", linux_x64);
149 case.addCompareOutput(
150 \\export fn _start() noreturn {
151 \\ add(3, 4);
152 \\
153 \\ exit();
154 \\}
155 \\
156 \\fn add(a: u32, b: u32) void {
157 \\ if (a + b != 7) unreachable;
158 \\}
159 \\
160 \\fn exit() noreturn {
161 \\ asm volatile ("syscall"
162 \\ :
163 \\ : [number] "{rax}" (231),
164 \\ [arg1] "{rdi}" (0)
165 \\ : "rcx", "r11", "memory"
166 \\ );
167 \\ unreachable;
168 \\}
169 ,
170 "",
171 );
172 }
173 {
174 var case = ctx.exe("assert function", linux_x64);
175 case.addCompareOutput(
176 \\export fn _start() noreturn {
177 \\ add(3, 4);
178 \\
179 \\ exit();
180 \\}
181 \\
182 \\fn add(a: u32, b: u32) void {
183 \\ assert(a + b == 7);
184 \\}
185 \\
186 \\pub fn assert(ok: bool) void {
187 \\ if (!ok) unreachable; // assertion failure
188 \\}
189 \\
190 \\fn exit() noreturn {
191 \\ asm volatile ("syscall"
192 \\ :
193 \\ : [number] "{rax}" (231),
194 \\ [arg1] "{rdi}" (0)
195 \\ : "rcx", "r11", "memory"
196 \\ );
197 \\ unreachable;
198 \\}
199 ,
200 "",
201 );
202 case.addCompareOutput(
203 \\export fn _start() noreturn {
204 \\ add(100, 200);
205 \\
206 \\ exit();
207 \\}
208 \\
209 \\fn add(a: u32, b: u32) void {
210 \\ assert(a + b == 300);
211 \\}
212 \\
213 \\pub fn assert(ok: bool) void {
214 \\ if (!ok) unreachable; // assertion failure
215 \\}
216 \\
217 \\fn exit() noreturn {
218 \\ asm volatile ("syscall"
219 \\ :
220 \\ : [number] "{rax}" (231),
221 \\ [arg1] "{rdi}" (0)
222 \\ : "rcx", "r11", "memory"
223 \\ );
224 \\ unreachable;
225 \\}
226 ,
227 "",
228 );
229 }
28}230}
test/stage2/compile_errors.zig+92-14
...@@ -1,55 +1,133 @@...@@ -1,55 +1,133 @@
1const TestContext = @import("../../src-self-hosted/test.zig").TestContext;1const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
2const std = @import("std");
3
4const ErrorMsg = @import("../../src-self-hosted/Module.zig").ErrorMsg;
5
6const linux_x64 = std.zig.CrossTarget{
7 .cpu_arch = .x86_64,
8 .os_tag = .linux,
9};
210
3pub fn addCases(ctx: *TestContext) !void {11pub fn addCases(ctx: *TestContext) !void {
4 // TODO: re-enable these tests.12 ctx.compileErrorZIR("call undefined local", linux_x64,
5 // https://github.com/ziglang/zig/issues/136413 \\@noreturn = primitive(noreturn)
14 \\
15 \\@start_fnty = fntype([], @noreturn, cc=Naked)
16 \\@start = fn(@start_fnty, {
17 \\ %0 = call(%test, [])
18 \\})
19 // TODO: address inconsistency in this message and the one in the next test
20 , &[_][]const u8{":5:13: error: unrecognized identifier: %test"});
21
22 ctx.compileErrorZIR("call with non-existent target", linux_x64,
23 \\@noreturn = primitive(noreturn)
24 \\
25 \\@start_fnty = fntype([], @noreturn, cc=Naked)
26 \\@start = fn(@start_fnty, {
27 \\ %0 = call(@notafunc, [])
28 \\})
29 \\@0 = str("_start")
30 \\@1 = export(@0, "start")
31 , &[_][]const u8{":5:13: error: decl 'notafunc' not found"});
32
33 // TODO: this error should occur at the call site, not the fntype decl
34 ctx.compileErrorZIR("call naked function", linux_x64,
35 \\@noreturn = primitive(noreturn)
36 \\
37 \\@start_fnty = fntype([], @noreturn, cc=Naked)
38 \\@s = fn(@start_fnty, {})
39 \\@start = fn(@start_fnty, {
40 \\ %0 = call(@s, [])
41 \\})
42 \\@0 = str("_start")
43 \\@1 = export(@0, "start")
44 , &[_][]const u8{":4:9: error: unable to call function with naked calling convention"});
45
46 ctx.incrementalFailureZIR("exported symbol collision", linux_x64,
47 \\@noreturn = primitive(noreturn)
48 \\
49 \\@start_fnty = fntype([], @noreturn)
50 \\@start = fn(@start_fnty, {})
51 \\
52 \\@0 = str("_start")
53 \\@1 = export(@0, "start")
54 \\@2 = export(@0, "start")
55 , &[_][]const u8{":8:13: error: exported symbol collision: _start"},
56 \\@noreturn = primitive(noreturn)
57 \\
58 \\@start_fnty = fntype([], @noreturn)
59 \\@start = fn(@start_fnty, {})
60 \\
61 \\@0 = str("_start")
62 \\@1 = export(@0, "start")
63 );
664
7 //try ctx.testCompileError(65 ctx.compileError("function redefinition", linux_x64,
66 \\fn entry() void {}
67 \\fn entry() void {}
68 , &[_][]const u8{":2:4: error: redefinition of 'entry'"});
69
70 //ctx.incrementalFailure("function redefinition", linux_x64,
71 // \\fn entry() void {}
72 // \\fn entry() void {}
73 //, &[_][]const u8{":2:4: error: redefinition of 'entry'"},
74 // \\fn entry() void {}
75 //);
76
77 //// TODO: need to make sure this works with other variants of export.
78 //ctx.incrementalFailure("exported symbol collision", linux_x64,
79 // \\export fn entry() void {}
8 // \\export fn entry() void {}80 // \\export fn entry() void {}
81 //, &[_][]const u8{":2:11: error: redefinition of 'entry'"},
9 // \\export fn entry() void {}82 // \\export fn entry() void {}
10 //, "1.zig", 2, 8, "exported symbol collision: 'entry'");83 //);
1184
12 //try ctx.testCompileError(85 // ctx.incrementalFailure("missing function name", linux_x64,
13 // \\fn() void {}86 // \\fn() void {}
14 //, "1.zig", 1, 1, "missing function name");87 // , &[_][]const u8{":1:3: error: missing function name"},
88 // \\fn a() void {}
89 // );
90
91 // TODO: re-enable these tests.
92 // https://github.com/ziglang/zig/issues/1364
1593
16 //try ctx.testCompileError(94 //ctx.testCompileError(
17 // \\comptime {95 // \\comptime {
18 // \\ return;96 // \\ return;
19 // \\}97 // \\}
20 //, "1.zig", 2, 5, "return expression outside function definition");98 //, "1.zig", 2, 5, "return expression outside function definition");
2199
22 //try ctx.testCompileError(100 //ctx.testCompileError(
23 // \\export fn entry() void {101 // \\export fn entry() void {
24 // \\ defer return;102 // \\ defer return;
25 // \\}103 // \\}
26 //, "1.zig", 2, 11, "cannot return from defer expression");104 //, "1.zig", 2, 11, "cannot return from defer expression");
27105
28 //try ctx.testCompileError(106 //ctx.testCompileError(
29 // \\export fn entry() c_int {107 // \\export fn entry() c_int {
30 // \\ return 36893488147419103232;108 // \\ return 36893488147419103232;
31 // \\}109 // \\}
32 //, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");110 //, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
33111
34 //try ctx.testCompileError(112 //ctx.testCompileError(
35 // \\comptime {113 // \\comptime {
36 // \\ var a: *align(4) align(4) i32 = 0;114 // \\ var a: *align(4) align(4) i32 = 0;
37 // \\}115 // \\}
38 //, "1.zig", 2, 22, "Extra align qualifier");116 //, "1.zig", 2, 22, "Extra align qualifier");
39117
40 //try ctx.testCompileError(118 //ctx.testCompileError(
41 // \\comptime {119 // \\comptime {
42 // \\ var b: *const const i32 = 0;120 // \\ var b: *const const i32 = 0;
43 // \\}121 // \\}
44 //, "1.zig", 2, 19, "Extra align qualifier");122 //, "1.zig", 2, 19, "Extra align qualifier");
45123
46 //try ctx.testCompileError(124 //ctx.testCompileError(
47 // \\comptime {125 // \\comptime {
48 // \\ var c: *volatile volatile i32 = 0;126 // \\ var c: *volatile volatile i32 = 0;
49 // \\}127 // \\}
50 //, "1.zig", 2, 22, "Extra align qualifier");128 //, "1.zig", 2, 22, "Extra align qualifier");
51129
52 //try ctx.testCompileError(130 //ctx.testCompileError(
53 // \\comptime {131 // \\comptime {
54 // \\ var d: *allowzero allowzero i32 = 0;132 // \\ var d: *allowzero allowzero i32 = 0;
55 // \\}133 // \\}
test/stage2/test.zig+2-1
...@@ -3,5 +3,6 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext;...@@ -3,5 +3,6 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3pub fn addCases(ctx: *TestContext) !void {3pub fn addCases(ctx: *TestContext) !void {
4 try @import("compile_errors.zig").addCases(ctx);4 try @import("compile_errors.zig").addCases(ctx);
5 try @import("compare_output.zig").addCases(ctx);5 try @import("compare_output.zig").addCases(ctx);
6 @import("zir.zig").addCases(ctx);6 try @import("zir.zig").addCases(ctx);
7 try @import("cbe.zig").addCases(ctx);
7}8}
test/stage2/zir.zig+157-338
...@@ -8,33 +8,31 @@ const linux_x64 = std.zig.CrossTarget{...@@ -8,33 +8,31 @@ const linux_x64 = std.zig.CrossTarget{
8 .os_tag = .linux,8 .os_tag = .linux,
9};9};
1010
11pub fn addCases(ctx: *TestContext) void {11pub fn addCases(ctx: *TestContext) !void {
12 ctx.addZIRTransform("referencing decls which appear later in the file", linux_x64,12 ctx.transformZIR("referencing decls which appear later in the file", linux_x64,
13 \\@void = primitive(void)13 \\@void = primitive(void)
14 \\@fnty = fntype([], @void, cc=C)14 \\@fnty = fntype([], @void, cc=C)
15 \\15 \\
16 \\@9 = str("entry")16 \\@9 = str("entry")
17 \\@10 = ref(@9)17 \\@11 = export(@9, "entry")
18 \\@11 = export(@10, @entry)
19 \\18 \\
20 \\@entry = fn(@fnty, {19 \\@entry = fn(@fnty, {
21 \\ %11 = return()20 \\ %11 = returnvoid()
22 \\})21 \\})
23 ,22 ,
24 \\@void = primitive(void)23 \\@void = primitive(void)
25 \\@fnty = fntype([], @void, cc=C)24 \\@fnty = fntype([], @void, cc=C)
26 \\@9 = str("entry")25 \\@9 = declref("9__anon_0")
27 \\@10 = ref(@9)26 \\@9__anon_0 = str("entry")
28 \\@unnamed$6 = str("entry")27 \\@unnamed$4 = str("entry")
29 \\@unnamed$7 = ref(@unnamed$6)28 \\@unnamed$5 = export(@unnamed$4, "entry")
30 \\@unnamed$8 = export(@unnamed$7, @entry)29 \\@unnamed$6 = fntype([], @void, cc=C)
31 \\@unnamed$10 = fntype([], @void, cc=C)30 \\@entry = fn(@unnamed$6, {
32 \\@entry = fn(@unnamed$10, {31 \\ %0 = returnvoid()
33 \\ %0 = return()
34 \\})32 \\})
35 \\33 \\
36 );34 );
37 ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,35 ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64,
38 \\@void = primitive(void)36 \\@void = primitive(void)
39 \\@usize = primitive(usize)37 \\@usize = primitive(usize)
40 \\@fnty = fntype([], @void, cc=C)38 \\@fnty = fntype([], @void, cc=C)
...@@ -45,11 +43,10 @@ pub fn addCases(ctx: *TestContext) void {...@@ -45,11 +43,10 @@ pub fn addCases(ctx: *TestContext) void {
45 \\43 \\
46 \\@entry = fn(@fnty, {44 \\@entry = fn(@fnty, {
47 \\ %a = str("\x32\x08\x01\x0a")45 \\ %a = str("\x32\x08\x01\x0a")
48 \\ %aref = ref(%a)46 \\ %eptr0 = elemptr(%a, @0)
49 \\ %eptr0 = elemptr(%aref, @0)47 \\ %eptr1 = elemptr(%a, @1)
50 \\ %eptr1 = elemptr(%aref, @1)48 \\ %eptr2 = elemptr(%a, @2)
51 \\ %eptr2 = elemptr(%aref, @2)49 \\ %eptr3 = elemptr(%a, @3)
52 \\ %eptr3 = elemptr(%aref, @3)
53 \\ %v0 = deref(%eptr0)50 \\ %v0 = deref(%eptr0)
54 \\ %v1 = deref(%eptr1)51 \\ %v1 = deref(%eptr1)
55 \\ %v2 = deref(%eptr2)52 \\ %v2 = deref(%eptr2)
...@@ -61,15 +58,14 @@ pub fn addCases(ctx: *TestContext) void {...@@ -61,15 +58,14 @@ pub fn addCases(ctx: *TestContext) void {
61 \\ %expected = int(69)58 \\ %expected = int(69)
62 \\ %ok = cmp(%result, eq, %expected)59 \\ %ok = cmp(%result, eq, %expected)
63 \\ %10 = condbr(%ok, {60 \\ %10 = condbr(%ok, {
64 \\ %11 = return()61 \\ %11 = returnvoid()
65 \\ }, {62 \\ }, {
66 \\ %12 = breakpoint()63 \\ %12 = breakpoint()
67 \\ })64 \\ })
68 \\})65 \\})
69 \\66 \\
70 \\@9 = str("entry")67 \\@9 = str("entry")
71 \\@10 = ref(@9)68 \\@11 = export(@9, "entry")
72 \\@11 = export(@10, @entry)
73 ,69 ,
74 \\@void = primitive(void)70 \\@void = primitive(void)
75 \\@fnty = fntype([], @void, cc=C)71 \\@fnty = fntype([], @void, cc=C)
...@@ -77,65 +73,62 @@ pub fn addCases(ctx: *TestContext) void {...@@ -77,65 +73,62 @@ pub fn addCases(ctx: *TestContext) void {
77 \\@1 = int(1)73 \\@1 = int(1)
78 \\@2 = int(2)74 \\@2 = int(2)
79 \\@3 = int(3)75 \\@3 = int(3)
80 \\@unnamed$7 = fntype([], @void, cc=C)76 \\@unnamed$6 = fntype([], @void, cc=C)
81 \\@entry = fn(@unnamed$7, {77 \\@entry = fn(@unnamed$6, {
82 \\ %0 = return()78 \\ %0 = returnvoid()
83 \\})79 \\})
84 \\@a = str("2\x08\x01\n")80 \\@entry__anon_1 = str("2\x08\x01\n")
85 \\@9 = str("entry")81 \\@9 = declref("9__anon_0")
86 \\@10 = ref(@9)82 \\@9__anon_0 = str("entry")
87 \\@unnamed$14 = str("entry")83 \\@unnamed$11 = str("entry")
88 \\@unnamed$15 = ref(@unnamed$14)84 \\@unnamed$12 = export(@unnamed$11, "entry")
89 \\@unnamed$16 = export(@unnamed$15, @entry)
90 \\85 \\
91 );86 );
9287
93 {88 {
94 var case = ctx.addZIRMulti("reference cycle with compile error in the cycle", linux_x64);89 var case = ctx.objZIR("reference cycle with compile error in the cycle", linux_x64);
95 case.addZIR(90 case.addTransform(
96 \\@void = primitive(void)91 \\@void = primitive(void)
97 \\@fnty = fntype([], @void, cc=C)92 \\@fnty = fntype([], @void, cc=C)
98 \\93 \\
99 \\@9 = str("entry")94 \\@9 = str("entry")
100 \\@10 = ref(@9)95 \\@11 = export(@9, "entry")
101 \\@11 = export(@10, @entry)
102 \\96 \\
103 \\@entry = fn(@fnty, {97 \\@entry = fn(@fnty, {
104 \\ %0 = call(@a, [])98 \\ %0 = call(@a, [])
105 \\ %1 = return()99 \\ %1 = returnvoid()
106 \\})100 \\})
107 \\101 \\
108 \\@a = fn(@fnty, {102 \\@a = fn(@fnty, {
109 \\ %0 = call(@b, [])103 \\ %0 = call(@b, [])
110 \\ %1 = return()104 \\ %1 = returnvoid()
111 \\})105 \\})
112 \\106 \\
113 \\@b = fn(@fnty, {107 \\@b = fn(@fnty, {
114 \\ %0 = call(@a, [])108 \\ %0 = call(@a, [])
115 \\ %1 = return()109 \\ %1 = returnvoid()
116 \\})110 \\})
117 ,111 ,
118 \\@void = primitive(void)112 \\@void = primitive(void)
119 \\@fnty = fntype([], @void, cc=C)113 \\@fnty = fntype([], @void, cc=C)
120 \\@9 = str("entry")114 \\@9 = declref("9__anon_0")
121 \\@10 = ref(@9)115 \\@9__anon_0 = str("entry")
122 \\@unnamed$6 = str("entry")116 \\@unnamed$4 = str("entry")
123 \\@unnamed$7 = ref(@unnamed$6)117 \\@unnamed$5 = export(@unnamed$4, "entry")
124 \\@unnamed$8 = export(@unnamed$7, @entry)118 \\@unnamed$6 = fntype([], @void, cc=C)
125 \\@unnamed$12 = fntype([], @void, cc=C)119 \\@entry = fn(@unnamed$6, {
126 \\@entry = fn(@unnamed$12, {
127 \\ %0 = call(@a, [], modifier=auto)120 \\ %0 = call(@a, [], modifier=auto)
128 \\ %1 = return()121 \\ %1 = returnvoid()
129 \\})122 \\})
130 \\@unnamed$17 = fntype([], @void, cc=C)123 \\@unnamed$8 = fntype([], @void, cc=C)
131 \\@a = fn(@unnamed$17, {124 \\@a = fn(@unnamed$8, {
132 \\ %0 = call(@b, [], modifier=auto)125 \\ %0 = call(@b, [], modifier=auto)
133 \\ %1 = return()126 \\ %1 = returnvoid()
134 \\})127 \\})
135 \\@unnamed$22 = fntype([], @void, cc=C)128 \\@unnamed$10 = fntype([], @void, cc=C)
136 \\@b = fn(@unnamed$22, {129 \\@b = fn(@unnamed$10, {
137 \\ %0 = call(@a, [], modifier=auto)130 \\ %0 = call(@a, [], modifier=auto)
138 \\ %1 = return()131 \\ %1 = returnvoid()
139 \\})132 \\})
140 \\133 \\
141 );134 );
...@@ -145,65 +138,62 @@ pub fn addCases(ctx: *TestContext) void {...@@ -145,65 +138,62 @@ pub fn addCases(ctx: *TestContext) void {
145 \\@fnty = fntype([], @void, cc=C)138 \\@fnty = fntype([], @void, cc=C)
146 \\139 \\
147 \\@9 = str("entry")140 \\@9 = str("entry")
148 \\@10 = ref(@9)141 \\@11 = export(@9, "entry")
149 \\@11 = export(@10, @entry)
150 \\142 \\
151 \\@entry = fn(@fnty, {143 \\@entry = fn(@fnty, {
152 \\ %0 = call(@a, [])144 \\ %0 = call(@a, [])
153 \\ %1 = return()145 \\ %1 = returnvoid()
154 \\})146 \\})
155 \\147 \\
156 \\@a = fn(@fnty, {148 \\@a = fn(@fnty, {
157 \\ %0 = call(@b, [])149 \\ %0 = call(@b, [])
158 \\ %1 = return()150 \\ %1 = returnvoid()
159 \\})151 \\})
160 \\152 \\
161 \\@b = fn(@fnty, {153 \\@b = fn(@fnty, {
162 \\ %9 = compileerror("message")154 \\ %9 = compileerror("message")
163 \\ %0 = call(@a, [])155 \\ %0 = call(@a, [])
164 \\ %1 = return()156 \\ %1 = returnvoid()
165 \\})157 \\})
166 ,158 ,
167 &[_][]const u8{159 &[_][]const u8{
168 ":19:21: error: message",160 ":18:21: error: message",
169 },161 },
170 );162 );
171 // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are163 // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are
172 // referencing either of them. This tests that the cycle is detected, and the error164 // referencing either of them. This tests that the cycle is detected, and the error
173 // goes away.165 // goes away.
174 case.addZIR(166 case.addTransform(
175 \\@void = primitive(void)167 \\@void = primitive(void)
176 \\@fnty = fntype([], @void, cc=C)168 \\@fnty = fntype([], @void, cc=C)
177 \\169 \\
178 \\@9 = str("entry")170 \\@9 = str("entry")
179 \\@10 = ref(@9)171 \\@11 = export(@9, "entry")
180 \\@11 = export(@10, @entry)
181 \\172 \\
182 \\@entry = fn(@fnty, {173 \\@entry = fn(@fnty, {
183 \\ %1 = return()174 \\ %0 = returnvoid()
184 \\})175 \\})
185 \\176 \\
186 \\@a = fn(@fnty, {177 \\@a = fn(@fnty, {
187 \\ %0 = call(@b, [])178 \\ %0 = call(@b, [])
188 \\ %1 = return()179 \\ %1 = returnvoid()
189 \\})180 \\})
190 \\181 \\
191 \\@b = fn(@fnty, {182 \\@b = fn(@fnty, {
192 \\ %9 = compileerror("message")183 \\ %9 = compileerror("message")
193 \\ %0 = call(@a, [])184 \\ %0 = call(@a, [])
194 \\ %1 = return()185 \\ %1 = returnvoid()
195 \\})186 \\})
196 ,187 ,
197 \\@void = primitive(void)188 \\@void = primitive(void)
198 \\@fnty = fntype([], @void, cc=C)189 \\@fnty = fntype([], @void, cc=C)
199 \\@9 = str("entry")190 \\@9 = declref("9__anon_2")
200 \\@10 = ref(@9)191 \\@9__anon_2 = str("entry")
201 \\@unnamed$6 = str("entry")192 \\@unnamed$4 = str("entry")
202 \\@unnamed$7 = ref(@unnamed$6)193 \\@unnamed$5 = export(@unnamed$4, "entry")
203 \\@unnamed$8 = export(@unnamed$7, @entry)194 \\@unnamed$6 = fntype([], @void, cc=C)
204 \\@unnamed$10 = fntype([], @void, cc=C)195 \\@entry = fn(@unnamed$6, {
205 \\@entry = fn(@unnamed$10, {196 \\ %0 = returnvoid()
206 \\ %0 = return()
207 \\})197 \\})
208 \\198 \\
209 );199 );
...@@ -217,272 +207,101 @@ pub fn addCases(ctx: *TestContext) void {...@@ -217,272 +207,101 @@ pub fn addCases(ctx: *TestContext) void {
217 return;207 return;
218 }208 }
219209
220 ctx.addZIRCompareOutput(210 ctx.compareOutputZIR("hello world ZIR",
221 "hello world ZIR, update msg",211 \\@noreturn = primitive(noreturn)
222 &[_][]const u8{212 \\@void = primitive(void)
223 \\@noreturn = primitive(noreturn)213 \\@usize = primitive(usize)
224 \\@void = primitive(void)214 \\@0 = int(0)
225 \\@usize = primitive(usize)215 \\@1 = int(1)
226 \\@0 = int(0)216 \\@2 = int(2)
227 \\@1 = int(1)217 \\@3 = int(3)
228 \\@2 = int(2)218 \\
229 \\@3 = int(3)219 \\@msg = str("Hello, world!\n")
230 \\220 \\
231 \\@syscall_array = str("syscall")221 \\@start_fnty = fntype([], @noreturn, cc=Naked)
232 \\@sysoutreg_array = str("={rax}")222 \\@start = fn(@start_fnty, {
233 \\@rax_array = str("{rax}")223 \\ %SYS_exit_group = int(231)
234 \\@rdi_array = str("{rdi}")224 \\ %exit_code = as(@usize, @0)
235 \\@rcx_array = str("rcx")225 \\
236 \\@r11_array = str("r11")226 \\ %syscall = str("syscall")
237 \\@rdx_array = str("{rdx}")227 \\ %sysoutreg = str("={rax}")
238 \\@rsi_array = str("{rsi}")228 \\ %rax = str("{rax}")
239 \\@memory_array = str("memory")229 \\ %rdi = str("{rdi}")
240 \\@len_array = str("len")230 \\ %rcx = str("rcx")
241 \\231 \\ %rdx = str("{rdx}")
242 \\@msg = str("Hello, world!\n")232 \\ %rsi = str("{rsi}")
243 \\233 \\ %r11 = str("r11")
244 \\@start_fnty = fntype([], @noreturn, cc=Naked)234 \\ %memory = str("memory")
245 \\@start = fn(@start_fnty, {235 \\
246 \\ %SYS_exit_group = int(231)236 \\ %SYS_write = as(@usize, @1)
247 \\ %exit_code = as(@usize, @0)237 \\ %STDOUT_FILENO = as(@usize, @1)
248 \\238 \\
249 \\ %syscall = ref(@syscall_array)239 \\ %msg_addr = ptrtoint(@msg)
250 \\ %sysoutreg = ref(@sysoutreg_array)240 \\
251 \\ %rax = ref(@rax_array)241 \\ %len_name = str("len")
252 \\ %rdi = ref(@rdi_array)242 \\ %msg_len_ptr = fieldptr(@msg, %len_name)
253 \\ %rcx = ref(@rcx_array)243 \\ %msg_len = deref(%msg_len_ptr)
254 \\ %rdx = ref(@rdx_array)244 \\ %rc_write = asm(%syscall, @usize,
255 \\ %rsi = ref(@rsi_array)245 \\ volatile=1,
256 \\ %r11 = ref(@r11_array)246 \\ output=%sysoutreg,
257 \\ %memory = ref(@memory_array)247 \\ inputs=[%rax, %rdi, %rsi, %rdx],
258 \\248 \\ clobbers=[%rcx, %r11, %memory],
259 \\ %SYS_write = as(@usize, @1)249 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
260 \\ %STDOUT_FILENO = as(@usize, @1)250 \\
261 \\251 \\ %rc_exit = asm(%syscall, @usize,
262 \\ %msg_ptr = ref(@msg)252 \\ volatile=1,
263 \\ %msg_addr = ptrtoint(%msg_ptr)253 \\ output=%sysoutreg,
264 \\254 \\ inputs=[%rax, %rdi],
265 \\ %len_name = ref(@len_array)255 \\ clobbers=[%rcx, %r11, %memory],
266 \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)256 \\ args=[%SYS_exit_group, %exit_code])
267 \\ %msg_len = deref(%msg_len_ptr)257 \\
268 \\ %rc_write = asm(%syscall, @usize,258 \\ %99 = unreachable()
269 \\ volatile=1,259 \\});
270 \\ output=%sysoutreg,260 \\
271 \\ inputs=[%rax, %rdi, %rsi, %rdx],261 \\@9 = str("_start")
272 \\ clobbers=[%rcx, %r11, %memory],262 \\@11 = export(@9, "start")
273 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])263 ,
274 \\264 \\Hello, world!
275 \\ %rc_exit = asm(%syscall, @usize,265 \\
276 \\ volatile=1,
277 \\ output=%sysoutreg,
278 \\ inputs=[%rax, %rdi],
279 \\ clobbers=[%rcx, %r11, %memory],
280 \\ args=[%SYS_exit_group, %exit_code])
281 \\
282 \\ %99 = unreachable()
283 \\});
284 \\
285 \\@9 = str("_start")
286 \\@10 = ref(@9)
287 \\@11 = export(@10, @start)
288 ,
289 \\@noreturn = primitive(noreturn)
290 \\@void = primitive(void)
291 \\@usize = primitive(usize)
292 \\@0 = int(0)
293 \\@1 = int(1)
294 \\@2 = int(2)
295 \\@3 = int(3)
296 \\
297 \\@syscall_array = str("syscall")
298 \\@sysoutreg_array = str("={rax}")
299 \\@rax_array = str("{rax}")
300 \\@rdi_array = str("{rdi}")
301 \\@rcx_array = str("rcx")
302 \\@r11_array = str("r11")
303 \\@rdx_array = str("{rdx}")
304 \\@rsi_array = str("{rsi}")
305 \\@memory_array = str("memory")
306 \\@len_array = str("len")
307 \\
308 \\@msg = str("Hello, world!\n")
309 \\@msg2 = str("HELL WORLD\n")
310 \\
311 \\@start_fnty = fntype([], @noreturn, cc=Naked)
312 \\@start = fn(@start_fnty, {
313 \\ %SYS_exit_group = int(231)
314 \\ %exit_code = as(@usize, @0)
315 \\
316 \\ %syscall = ref(@syscall_array)
317 \\ %sysoutreg = ref(@sysoutreg_array)
318 \\ %rax = ref(@rax_array)
319 \\ %rdi = ref(@rdi_array)
320 \\ %rcx = ref(@rcx_array)
321 \\ %rdx = ref(@rdx_array)
322 \\ %rsi = ref(@rsi_array)
323 \\ %r11 = ref(@r11_array)
324 \\ %memory = ref(@memory_array)
325 \\
326 \\ %SYS_write = as(@usize, @1)
327 \\ %STDOUT_FILENO = as(@usize, @1)
328 \\
329 \\ %msg_ptr = ref(@msg2)
330 \\ %msg_addr = ptrtoint(%msg_ptr)
331 \\
332 \\ %len_name = ref(@len_array)
333 \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)
334 \\ %msg_len = deref(%msg_len_ptr)
335 \\ %rc_write = asm(%syscall, @usize,
336 \\ volatile=1,
337 \\ output=%sysoutreg,
338 \\ inputs=[%rax, %rdi, %rsi, %rdx],
339 \\ clobbers=[%rcx, %r11, %memory],
340 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
341 \\
342 \\ %rc_exit = asm(%syscall, @usize,
343 \\ volatile=1,
344 \\ output=%sysoutreg,
345 \\ inputs=[%rax, %rdi],
346 \\ clobbers=[%rcx, %r11, %memory],
347 \\ args=[%SYS_exit_group, %exit_code])
348 \\
349 \\ %99 = unreachable()
350 \\});
351 \\
352 \\@9 = str("_start")
353 \\@10 = ref(@9)
354 \\@11 = export(@10, @start)
355 ,
356 \\@noreturn = primitive(noreturn)
357 \\@void = primitive(void)
358 \\@usize = primitive(usize)
359 \\@0 = int(0)
360 \\@1 = int(1)
361 \\@2 = int(2)
362 \\@3 = int(3)
363 \\
364 \\@syscall_array = str("syscall")
365 \\@sysoutreg_array = str("={rax}")
366 \\@rax_array = str("{rax}")
367 \\@rdi_array = str("{rdi}")
368 \\@rcx_array = str("rcx")
369 \\@r11_array = str("r11")
370 \\@rdx_array = str("{rdx}")
371 \\@rsi_array = str("{rsi}")
372 \\@memory_array = str("memory")
373 \\@len_array = str("len")
374 \\
375 \\@msg = str("Hello, world!\n")
376 \\@msg2 = str("Editing the same msg2 decl but this time with a much longer message which will\ncause the data to need to be relocated in virtual address space.\n")
377 \\
378 \\@start_fnty = fntype([], @noreturn, cc=Naked)
379 \\@start = fn(@start_fnty, {
380 \\ %SYS_exit_group = int(231)
381 \\ %exit_code = as(@usize, @0)
382 \\
383 \\ %syscall = ref(@syscall_array)
384 \\ %sysoutreg = ref(@sysoutreg_array)
385 \\ %rax = ref(@rax_array)
386 \\ %rdi = ref(@rdi_array)
387 \\ %rcx = ref(@rcx_array)
388 \\ %rdx = ref(@rdx_array)
389 \\ %rsi = ref(@rsi_array)
390 \\ %r11 = ref(@r11_array)
391 \\ %memory = ref(@memory_array)
392 \\
393 \\ %SYS_write = as(@usize, @1)
394 \\ %STDOUT_FILENO = as(@usize, @1)
395 \\
396 \\ %msg_ptr = ref(@msg2)
397 \\ %msg_addr = ptrtoint(%msg_ptr)
398 \\
399 \\ %len_name = ref(@len_array)
400 \\ %msg_len_ptr = fieldptr(%msg_ptr, %len_name)
401 \\ %msg_len = deref(%msg_len_ptr)
402 \\ %rc_write = asm(%syscall, @usize,
403 \\ volatile=1,
404 \\ output=%sysoutreg,
405 \\ inputs=[%rax, %rdi, %rsi, %rdx],
406 \\ clobbers=[%rcx, %r11, %memory],
407 \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
408 \\
409 \\ %rc_exit = asm(%syscall, @usize,
410 \\ volatile=1,
411 \\ output=%sysoutreg,
412 \\ inputs=[%rax, %rdi],
413 \\ clobbers=[%rcx, %r11, %memory],
414 \\ args=[%SYS_exit_group, %exit_code])
415 \\
416 \\ %99 = unreachable()
417 \\});
418 \\
419 \\@9 = str("_start")
420 \\@10 = ref(@9)
421 \\@11 = export(@10, @start)
422 },
423 &[_][]const u8{
424 \\Hello, world!
425 \\
426 ,
427 \\HELL WORLD
428 \\
429 ,
430 \\Editing the same msg2 decl but this time with a much longer message which will
431 \\cause the data to need to be relocated in virtual address space.
432 \\
433 },
434 );266 );
435267
436 ctx.addZIRCompareOutput(268 ctx.compareOutputZIR("function call with no args no return value",
437 "function call with no args no return value",269 \\@noreturn = primitive(noreturn)
438 &[_][]const u8{270 \\@void = primitive(void)
439 \\@noreturn = primitive(noreturn)271 \\@usize = primitive(usize)
440 \\@void = primitive(void)272 \\@0 = int(0)
441 \\@usize = primitive(usize)273 \\@1 = int(1)
442 \\@0 = int(0)274 \\@2 = int(2)
443 \\@1 = int(1)275 \\@3 = int(3)
444 \\@2 = int(2)276 \\
445 \\@3 = int(3)277 \\@exit0_fnty = fntype([], @noreturn)
446 \\278 \\@exit0 = fn(@exit0_fnty, {
447 \\@syscall_array = str("syscall")279 \\ %SYS_exit_group = int(231)
448 \\@sysoutreg_array = str("={rax}")280 \\ %exit_code = as(@usize, @0)
449 \\@rax_array = str("{rax}")281 \\
450 \\@rdi_array = str("{rdi}")282 \\ %syscall = str("syscall")
451 \\@rcx_array = str("rcx")283 \\ %sysoutreg = str("={rax}")
452 \\@r11_array = str("r11")284 \\ %rax = str("{rax}")
453 \\@memory_array = str("memory")285 \\ %rdi = str("{rdi}")
454 \\286 \\ %rcx = str("rcx")
455 \\@exit0_fnty = fntype([], @noreturn)287 \\ %r11 = str("r11")
456 \\@exit0 = fn(@exit0_fnty, {288 \\ %memory = str("memory")
457 \\ %SYS_exit_group = int(231)289 \\
458 \\ %exit_code = as(@usize, @0)290 \\ %rc = asm(%syscall, @usize,
459 \\291 \\ volatile=1,
460 \\ %syscall = ref(@syscall_array)292 \\ output=%sysoutreg,
461 \\ %sysoutreg = ref(@sysoutreg_array)293 \\ inputs=[%rax, %rdi],
462 \\ %rax = ref(@rax_array)294 \\ clobbers=[%rcx, %r11, %memory],
463 \\ %rdi = ref(@rdi_array)295 \\ args=[%SYS_exit_group, %exit_code])
464 \\ %rcx = ref(@rcx_array)296 \\
465 \\ %r11 = ref(@r11_array)297 \\ %99 = unreachable()
466 \\ %memory = ref(@memory_array)298 \\});
467 \\299 \\
468 \\ %rc = asm(%syscall, @usize,300 \\@start_fnty = fntype([], @noreturn, cc=Naked)
469 \\ volatile=1,301 \\@start = fn(@start_fnty, {
470 \\ output=%sysoutreg,302 \\ %0 = call(@exit0, [])
471 \\ inputs=[%rax, %rdi],303 \\})
472 \\ clobbers=[%rcx, %r11, %memory],304 \\@9 = str("_start")
473 \\ args=[%SYS_exit_group, %exit_code])305 \\@11 = export(@9, "start")
474 \\306 , "");
475 \\ %99 = unreachable()
476 \\});
477 \\
478 \\@start_fnty = fntype([], @noreturn, cc=Naked)
479 \\@start = fn(@start_fnty, {
480 \\ %0 = call(@exit0, [])
481 \\})
482 \\@9 = str("_start")
483 \\@10 = ref(@9)
484 \\@11 = export(@10, @start)
485 },
486 &[_][]const u8{""},
487 );
488}307}
test/standalone/guess_number/main.zig+1-1
...@@ -4,7 +4,7 @@ const io = std.io;...@@ -4,7 +4,7 @@ const io = std.io;
4const fmt = std.fmt;4const fmt = std.fmt;
55
6pub fn main() !void {6pub fn main() !void {
7 const stdout = io.getStdOut().outStream();7 const stdout = io.getStdOut().writer();
8 const stdin = io.getStdIn();8 const stdin = io.getStdIn();
99
10 try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{});10 try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{});
test/tests.zig+1
...@@ -537,6 +537,7 @@ pub fn addPkgTests(...@@ -537,6 +537,7 @@ pub fn addPkgTests(
537 these_tests.enable_qemu = is_qemu_enabled;537 these_tests.enable_qemu = is_qemu_enabled;
538 these_tests.enable_wasmtime = is_wasmtime_enabled;538 these_tests.enable_wasmtime = is_wasmtime_enabled;
539 these_tests.glibc_multi_install_dir = glibc_dir;539 these_tests.glibc_multi_install_dir = glibc_dir;
540 these_tests.addIncludeDir("test");
540541
541 step.dependOn(&these_tests.step);542 step.dependOn(&these_tests.step);
542 }543 }
test/translate_c.zig+42-17
...@@ -3,6 +3,31 @@ const std = @import("std");...@@ -3,6 +3,31 @@ const std = @import("std");
3const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
44
5pub fn addCases(cases: *tests.TranslateCContext) void {5pub fn addCases(cases: *tests.TranslateCContext) void {
6 cases.add("initializer list macro",
7 \\typedef struct Color {
8 \\ unsigned char r;
9 \\ unsigned char g;
10 \\ unsigned char b;
11 \\ unsigned char a;
12 \\} Color;
13 \\#define CLITERAL(type) (type)
14 \\#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
15 , &[_][]const u8{ // TODO properly translate this
16 \\pub const struct_Color = extern struct {
17 \\ r: u8,
18 \\ g: u8,
19 \\ b: u8,
20 \\ a: u8,
21 \\};
22 \\pub const Color = struct_Color;
23 ,
24 \\pub inline fn CLITERAL(type_1: anytype) @TypeOf(type_1) {
25 \\ return type_1;
26 \\}
27 ,
28 \\pub const LIGHTGRAY = @import("std").mem.zeroInit(CLITERAL(Color), .{ 200, 200, 200, 255 });
29 });
30
6 cases.add("complex switch",31 cases.add("complex switch",
7 \\int main() {32 \\int main() {
8 \\ int i = 2;33 \\ int i = 2;
...@@ -21,7 +46,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -21,7 +46,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
21 cases.add("correct semicolon after infixop",46 cases.add("correct semicolon after infixop",
22 \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0)47 \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0)
23 , &[_][]const u8{48 , &[_][]const u8{
24 \\pub inline fn __ferror_unlocked_body(_fp: var) @TypeOf(((_fp.*._flags) & _IO_ERR_SEEN) != 0) {49 \\pub inline fn __ferror_unlocked_body(_fp: anytype) @TypeOf(((_fp.*._flags) & _IO_ERR_SEEN) != 0) {
25 \\ return ((_fp.*._flags) & _IO_ERR_SEEN) != 0;50 \\ return ((_fp.*._flags) & _IO_ERR_SEEN) != 0;
26 \\}51 \\}
27 });52 });
...@@ -30,7 +55,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -30,7 +55,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
30 \\#define FOO(x) ((x >= 0) + (x >= 0))55 \\#define FOO(x) ((x >= 0) + (x >= 0))
31 \\#define BAR 1 && 2 > 456 \\#define BAR 1 && 2 > 4
32 , &[_][]const u8{57 , &[_][]const u8{
33 \\pub inline fn FOO(x: var) @TypeOf(@boolToInt(x >= 0) + @boolToInt(x >= 0)) {58 \\pub inline fn FOO(x: anytype) @TypeOf(@boolToInt(x >= 0) + @boolToInt(x >= 0)) {
34 \\ return @boolToInt(x >= 0) + @boolToInt(x >= 0);59 \\ return @boolToInt(x >= 0) + @boolToInt(x >= 0);
35 \\}60 \\}
36 ,61 ,
...@@ -81,7 +106,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -81,7 +106,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
81 \\ break :blk bar;106 \\ break :blk bar;
82 \\};107 \\};
83 ,108 ,
84 \\pub inline fn bar(x: var) @TypeOf(baz(1, 2)) {109 \\pub inline fn bar(x: anytype) @TypeOf(baz(1, 2)) {
85 \\ return blk: {110 \\ return blk: {
86 \\ _ = &x;111 \\ _ = &x;
87 \\ _ = 3;112 \\ _ = 3;
...@@ -1473,7 +1498,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1473,7 +1498,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1473 cases.add("macro pointer cast",1498 cases.add("macro pointer cast",
1474 \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)1499 \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
1475 , &[_][]const u8{1500 , &[_][]const u8{
1476 \\pub const NRF_GPIO = (if (@typeInfo(@TypeOf(NRF_GPIO_BASE)) == .Pointer) @ptrCast([*c]NRF_GPIO_Type, @alignCast(@alignOf([*c]NRF_GPIO_Type.Child), NRF_GPIO_BASE)) else if (@typeInfo(@TypeOf(NRF_GPIO_BASE)) == .Int and @typeInfo([*c]NRF_GPIO_Type) == .Pointer) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE));1501 \\pub const NRF_GPIO = (@import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE));
1477 });1502 });
14781503
1479 cases.add("basic macro function",1504 cases.add("basic macro function",
...@@ -1483,11 +1508,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1483,11 +1508,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1483 , &[_][]const u8{1508 , &[_][]const u8{
1484 \\pub extern var c: c_int;1509 \\pub extern var c: c_int;
1485 ,1510 ,
1486 \\pub inline fn BASIC(c_1: var) @TypeOf(c_1 * 2) {1511 \\pub inline fn BASIC(c_1: anytype) @TypeOf(c_1 * 2) {
1487 \\ return c_1 * 2;1512 \\ return c_1 * 2;
1488 \\}1513 \\}
1489 ,1514 ,
1490 \\pub inline fn FOO(L: var, b: var) @TypeOf(L + b) {1515 \\pub inline fn FOO(L: anytype, b: anytype) @TypeOf(L + b) {
1491 \\ return L + b;1516 \\ return L + b;
1492 \\}1517 \\}
1493 });1518 });
...@@ -2123,7 +2148,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2123,7 +2148,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2123 cases.add("macro call",2148 cases.add("macro call",
2124 \\#define CALL(arg) bar(arg)2149 \\#define CALL(arg) bar(arg)
2125 , &[_][]const u8{2150 , &[_][]const u8{
2126 \\pub inline fn CALL(arg: var) @TypeOf(bar(arg)) {2151 \\pub inline fn CALL(arg: anytype) @TypeOf(bar(arg)) {
2127 \\ return bar(arg);2152 \\ return bar(arg);
2128 \\}2153 \\}
2129 });2154 });
...@@ -2683,11 +2708,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2683,11 +2708,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2683 \\#define FOO(bar) baz((void *)(baz))2708 \\#define FOO(bar) baz((void *)(baz))
2684 \\#define BAR (void*) a2709 \\#define BAR (void*) a
2685 , &[_][]const u8{2710 , &[_][]const u8{
2686 \\pub inline fn FOO(bar: var) @TypeOf(baz((if (@typeInfo(@TypeOf(baz)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), baz)) else if (@typeInfo(@TypeOf(baz)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, baz) else @as(?*c_void, baz)))) {2711 \\pub inline fn FOO(bar: anytype) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) {
2687 \\ return baz((if (@typeInfo(@TypeOf(baz)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), baz)) else if (@typeInfo(@TypeOf(baz)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, baz) else @as(?*c_void, baz)));2712 \\ return baz((@import("std").meta.cast(?*c_void, baz)));
2688 \\}2713 \\}
2689 ,2714 ,
2690 \\pub const BAR = (if (@typeInfo(@TypeOf(a)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), a)) else if (@typeInfo(@TypeOf(a)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, a) else @as(?*c_void, a));2715 \\pub const BAR = (@import("std").meta.cast(?*c_void, a));
2691 });2716 });
26922717
2693 cases.add("macro conditional operator",2718 cases.add("macro conditional operator",
...@@ -2713,11 +2738,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2713,11 +2738,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2713 \\#define MIN(a, b) ((b) < (a) ? (b) : (a))2738 \\#define MIN(a, b) ((b) < (a) ? (b) : (a))
2714 \\#define MAX(a, b) ((b) > (a) ? (b) : (a))2739 \\#define MAX(a, b) ((b) > (a) ? (b) : (a))
2715 , &[_][]const u8{2740 , &[_][]const u8{
2716 \\pub inline fn MIN(a: var, b: var) @TypeOf(if (b < a) b else a) {2741 \\pub inline fn MIN(a: anytype, b: anytype) @TypeOf(if (b < a) b else a) {
2717 \\ return if (b < a) b else a;2742 \\ return if (b < a) b else a;
2718 \\}2743 \\}
2719 ,2744 ,
2720 \\pub inline fn MAX(a: var, b: var) @TypeOf(if (b > a) b else a) {2745 \\pub inline fn MAX(a: anytype, b: anytype) @TypeOf(if (b > a) b else a) {
2721 \\ return if (b > a) b else a;2746 \\ return if (b > a) b else a;
2722 \\}2747 \\}
2723 });2748 });
...@@ -2797,7 +2822,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2797,7 +2822,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2797 \\pub fn a() callconv(.C) void {}2822 \\pub fn a() callconv(.C) void {}
2798 \\pub fn b() callconv(.C) void {}2823 \\pub fn b() callconv(.C) void {}
2799 \\pub export fn c() void {}2824 \\pub export fn c() void {}
2800 \\pub fn foo() callconv(.C) void {}2825 \\pub fn foo(...) callconv(.C) void {}
2801 });2826 });
28022827
2803 cases.add("casting away const and volatile",2828 cases.add("casting away const and volatile",
...@@ -2905,8 +2930,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2905,8 +2930,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2905 \\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen)2930 \\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen)
2906 \\2931 \\
2907 , &[_][]const u8{2932 , &[_][]const u8{
2908 \\pub inline fn DefaultScreen(dpy: var) @TypeOf((if (@typeInfo(@TypeOf(dpy)) == .Pointer) @ptrCast(_XPrivDisplay, @alignCast(@alignOf(_XPrivDisplay.Child), dpy)) else if (@typeInfo(@TypeOf(dpy)) == .Int and @typeInfo(_XPrivDisplay) == .Pointer) @intToPtr(_XPrivDisplay, dpy) else @as(_XPrivDisplay, dpy)).*.default_screen) {2933 \\pub inline fn DefaultScreen(dpy: anytype) @TypeOf((@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen) {
2909 \\ return (if (@typeInfo(@TypeOf(dpy)) == .Pointer) @ptrCast(_XPrivDisplay, @alignCast(@alignOf(_XPrivDisplay.Child), dpy)) else if (@typeInfo(@TypeOf(dpy)) == .Int and @typeInfo(_XPrivDisplay) == .Pointer) @intToPtr(_XPrivDisplay, dpy) else @as(_XPrivDisplay, dpy)).*.default_screen;2934 \\ return (@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen;
2910 \\}2935 \\}
2911 });2936 });
29122937
...@@ -2914,9 +2939,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2914,9 +2939,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2914 \\#define NULL ((void*)0)2939 \\#define NULL ((void*)0)
2915 \\#define FOO ((int)0x8000)2940 \\#define FOO ((int)0x8000)
2916 , &[_][]const u8{2941 , &[_][]const u8{
2917 \\pub const NULL = (if (@typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, 0) else @as(?*c_void, 0));2942 \\pub const NULL = (@import("std").meta.cast(?*c_void, 0));
2918 ,2943 ,
2919 \\pub const FOO = (if (@typeInfo(c_int) == .Pointer) @intToPtr(c_int, 0x8000) else @as(c_int, 0x8000));2944 \\pub const FOO = (@import("std").meta.cast(c_int, 0x8000));
2920 });2945 });
29212946
2922 if (std.Target.current.abi == .msvc) {2947 if (std.Target.current.abi == .msvc) {