| author | |
| committer | |
| log | 3658dd5e89cd16c011bdc52d334c1308f440157b |
| tree | 09564ab2db65acc4a52d82bccbf0eb572fbc865f |
| parent | 68fe3e116d9c4bde67df990b8e0cbb3e70fc98b2 |
| parent | 596ca6cf70cf43c27e31bbcfc36bcdc70b13897a |
255 files changed, 16493 insertions(+), 7827 deletions(-)
.github/FUNDING.yml+1-1| ... | @@ -1 +1 @@ | ... | @@ -1 +1 @@ |
| 1 | github: [andrewrk] | 1 | github: [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 |
| 53 | set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries") | 53 | set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries") |
| 54 | set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation") | 54 | set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation") |
| 55 | set(ZIG_PREFER_CLANG_CPP_DYLIB off CACHE BOOL "Try to link against -lclang-cpp") | 55 | set(ZIG_PREFER_CLANG_CPP_DYLIB off CACHE BOOL "Try to link against -lclang-cpp") |
| 56 | set(ZIG_WORKAROUND_4799 off CACHE BOOL "workaround for https://github.com/ziglang/zig/issues/4799") | ||
| 57 | set(ZIG_WORKAROUND_POLLY_SO off CACHE STRING "workaround for https://github.com/ziglang/zig/issues/4799") | ||
| 56 | set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache if available") | 58 | set(ZIG_USE_CCACHE off CACHE BOOL "Use ccache if available") |
| 57 | 59 | ||
| 58 | if(CCACHE_PROGRAM AND ZIG_USE_CCACHE) | 60 | if(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}") |
| 89 | endif() | 91 | endif() |
| 90 | 92 | ||
| 93 | if(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") | ||
| 96 | endif() | ||
| 97 | |||
| 91 | set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zig_cpp") | 98 | set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zig_cpp") |
| 92 | 99 | ||
| 93 | # Handle multi-config builds and place each into a common lib. The VS generator | 100 | # 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 | ) |
| 293 | set(OPTIMIZED_C_SOURCES | 301 | set(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}) |
| 396 | set_target_properties(zig_cpp PROPERTIES | 404 | set_target_properties(zig_cpp PROPERTIES |
| 397 | COMPILE_FLAGS ${EXE_CFLAGS} | 405 | COMPILE_FLAGS ${EXE_CFLAGS} |
| 398 | ) | 406 | ) |
| 407 | |||
| 399 | target_link_libraries(zig_cpp LINK_PUBLIC | 408 | target_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 | ) |
| 413 | if(ZIG_WORKAROUND_POLLY_SO) | ||
| 414 | target_link_libraries(zig_cpp LINK_PUBLIC "-Wl,${ZIG_WORKAROUND_POLLY_SO}") | ||
| 415 | endif() | ||
| 404 | 416 | ||
| 405 | add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES}) | 417 | add_library(opt_c_util STATIC ${OPTIMIZED_C_SOURCES}) |
| 406 | set_target_properties(opt_c_util PROPERTIES | 418 | set_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, when | 152 | 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. |
| 154 | 154 | ||
| 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 which | 160 | * `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 it | 161 | 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 .. |
| 51 | make install | 51 | make install |
| 52 | ``` | 52 | ``` |
| 53 | 53 | ||
| 54 | Need help? [Troubleshooting Build Issues](https://github.com/ziglang/zig/wiki/Troubleshooting-Build-Issues) | ||
| 55 | |||
| 54 | ##### MacOS | 56 | ##### MacOS |
| 55 | 57 | ||
| 56 | ``` | 58 | ``` |
| ... | @@ -64,9 +66,11 @@ make install | ... | @@ -64,9 +66,11 @@ make install |
| 64 | 66 | ||
| 65 | You will now run into this issue: | 67 | You 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) |
| 69 | or | ||
| 70 | [error: unable to create target: 'Unable to find target for this triple (no targets are registered)'](https://github.com/ziglang/zig/issues/5055), | ||
| 71 | in which case try `-DZIG_WORKAROUND_4799=ON` | ||
| 67 | 72 | ||
| 68 | Please help upstream LLVM and Homebrew solve this issue, there is nothing Zig | 73 | Hopefully this will be fixed upstream with LLVM 10.0.1. |
| 69 | can do about it. See that issue for a workaround you can do in the meantime. | ||
| 70 | 74 | ||
| 71 | ##### Windows | 75 | ##### Windows |
| 72 | 76 |
build.zig+43-27| ... | @@ -34,26 +34,12 @@ pub fn build(b: *Builder) !void { | ... | @@ -34,26 +34,12 @@ pub fn build(b: *Builder) !void { |
| 34 | 34 | ||
| 35 | const test_step = b.step("test", "Run all the tests"); | 35 | const test_step = b.step("test", "Run all the tests"); |
| 36 | 36 | ||
| 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 harness | 38 | 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"); |
| 49 | 40 | ||
| 50 | const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); | 41 | const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); |
| 51 | 42 | ||
| 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 { |
| 63 | 49 | ||
| 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); | ||
| 69 | 53 | ||
| 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(); | ||
| 77 | 90 | ||
| 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 | } |
| 139 | 155 | ||
| 140 | fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void { | 156 | fn 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 | } |
| 179 | 195 | ||
| 180 | fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void { | 196 | fn 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 | } |
| 261 | 277 | ||
| 262 | fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { | 278 | fn 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 { |
| 324 | fn addCxxKnownPath( | 340 | fn 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 |
| 12 | 12 | ||
| 13 | sudo apt-get remove -y llvm-* | 13 | sudo apt-get remove -y llvm-* |
| 14 | sudo rm -rf /usr/local/* | 14 | sudo rm -rf /usr/local/* |
| 15 | sudo 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 | 15 | sudo 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 |
| 16 | 16 | ||
| 17 | QEMUBASE="qemu-linux-x86_64-5.0.0-49ee115552" | 17 | QEMUBASE="qemu-linux-x86_64-5.0.0-49ee115552" |
| 18 | wget https://ziglang.org/deps/$QEMUBASE.tar.xz | 18 | wget https://ziglang.org/deps/$QEMUBASE.tar.xz |
| ... | @@ -51,6 +51,10 @@ cd build | ... | @@ -51,6 +51,10 @@ cd build |
| 51 | cmake .. -DCMAKE_BUILD_TYPE=Release -GNinja | 51 | cmake .. -DCMAKE_BUILD_TYPE=Release -GNinja |
| 52 | ninja install | 52 | ninja install |
| 53 | ./zig build test -Denable-qemu -Denable-wasmtime | 53 | ./zig build test -Denable-qemu -Denable-wasmtime |
| 54 | |||
| 55 | # look for HTML errors | ||
| 56 | tidy -qe ../zig-cache/langref.html | ||
| 57 | |||
| 54 | VERSION="$(./zig version)" | 58 | VERSION="$(./zig version)" |
| 55 | 59 | ||
| 56 | if [ "${BUILD_REASON}" != "PullRequest" ]; then | 60 | if [ "${BUILD_REASON}" != "PullRequest" ]; then |
ci/azure/pipelines.yml+13-5| ... | @@ -40,12 +40,20 @@ jobs: | ... | @@ -40,12 +40,20 @@ jobs: |
| 40 | timeoutInMinutes: 360 | 40 | timeoutInMinutes: 360 |
| 41 | 41 | ||
| 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%\msys64 | 49 | @REM install updated filesystem package first without dependency checking |
| 45 | %CD:~0,2%\msys64\usr\bin\rm -rf %CD:~0,2%\msys64\.git | 50 | @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\Wbem | 51 | %CD:~0,2%\msys64\usr\bin\bash -lc "pacman --noconfirm -Sydd filesystem" |
| 47 | %CD:~0,2%\msys64\usr\bin\pacman --noconfirm -Syyuu | 52 | displayName: Workaround filesystem dash MSYS2 dependency issue |
| 48 | displayName: Install and Update MSYS2 | 53 | - 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@1 | 57 | - task: DownloadSecureFile@1 |
| 50 | inputs: | 58 | inputs: |
| 51 | secureFile: s3cfg | 59 | secureFile: s3cfg |
ci/azure/windows_msvc_install+1-1| ... | @@ -4,7 +4,7 @@ set -x | ... | @@ -4,7 +4,7 @@ set -x |
| 4 | set -e | 4 | set -e |
| 5 | 5 | ||
| 6 | pacman -Su --needed --noconfirm | 6 | pacman -Su --needed --noconfirm |
| 7 | pacman -S --needed --noconfirm wget p7zip python3-pip | 7 | pacman -S --needed --noconfirm wget p7zip python3-pip tar xz |
| 8 | pip install s3cmd | 8 | pip install s3cmd |
| 9 | wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz" | 9 | wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz" |
| 10 | tar xf llvm+clang+lld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz | 10 | tar 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 | }; |
| 214 | 214 | ||
| 215 | fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: var) anyerror { | 215 | fn 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 | } |
| 636 | 636 | ||
| 637 | fn writeEscaped(out: var, input: []const u8) !void { | 637 | fn 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("&"), | 640 | '&' => out.writeAll("&"), |
| ... | @@ -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 | } |
| 767 | 767 | ||
| 768 | fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Token, raw_src: []const u8) !void { | 768 | fn 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 | } |
| 979 | 980 | ||
| 980 | fn tokenizeAndPrint(docgen_tokenizer: *Tokenizer, out: var, source_token: Token) !void { | 981 | fn 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 | } |
| 984 | 985 | ||
| 985 | fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void { | 986 | fn 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; |
| 987 | 988 | ||
| 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 | } |
| 99 | 99 | ||
| 100 | #index { | 100 | #toc { |
| 101 | padding: 0 1em; | 101 | padding: 0 1em; |
| 102 | } | 102 | } |
| 103 | 103 | ||
| ... | @@ -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 | master | 191 | 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 @@ |
| 231 | const std = @import("std"); | 233 | const std = @import("std"); |
| 232 | 234 | ||
| 233 | pub fn main() !void { | 235 | pub 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 you | 241 | 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> |
| 244 | const 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#} | ||
| 333 | const print = @import("std").debug.print; | ||
| 245 | 334 | ||
| 246 | pub fn main() void { | 335 | pub 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: |
| 310 | const warn = std.debug.warn; | 411 | const print = std.debug.print; |
| 311 | const std = @import("std"); | 412 | const std = @import("std"); |
| 312 | const os = std.os; | 413 | const os = std.os; |
| 313 | const assert = std.debug.assert; | 414 | const assert = std.debug.assert; |
| ... | @@ -315,14 +416,14 @@ const assert = std.debug.assert; | ... | @@ -315,14 +416,14 @@ const assert = std.debug.assert; |
| 315 | pub fn main() void { | 416 | pub fn main() void { |
| 316 | // integers | 417 | // 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}); |
| 319 | 420 | ||
| 320 | // floats | 421 | // 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}); |
| 323 | 424 | ||
| 324 | // boolean | 425 | // 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); |
| 334 | 435 | ||
| 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); |
| 342 | 443 | ||
| 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 union | 449 | // error union |
| 349 | var number_or_error: anyerror!i32 = error.ArgNotFound; | 450 | var number_or_error: anyerror!i32 = error.ArgNotFound; |
| 350 | 451 | ||
| 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 | }); |
| 355 | 456 | ||
| 356 | number_or_error = 1234; | 457 | number_or_error = 1234; |
| 357 | 458 | ||
| 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#} |
| 997 | const warn = @import("std").debug.warn; | 1098 | const print = @import("std").debug.print; |
| 998 | 1099 | ||
| 999 | extern fn foo_strict(x: f64) f64; | 1100 | extern fn foo_strict(x: f64) f64; |
| 1000 | extern fn foo_optimized(x: f64) f64; | 1101 | extern fn foo_optimized(x: f64) f64; |
| 1001 | 1102 | ||
| 1002 | pub fn main() void { | 1103 | pub 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 | } |
| 1788 | 1889 | ||
| 1789 | fn dump(args: var) void { | 1890 | fn 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" { |
| 1849 | 1950 | ||
| 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 on | 1953 | 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"); |
| 2668 | 2769 | ||
| 2669 | pub fn main() void { | 2770 | pub 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 | } |
| 2675 | 2776 | ||
| 2676 | fn List(comptime T: type) type { | 2777 | fn 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 | } |
| 2720 | 2821 | ||
| 2721 | fn dump(args: var) void { | 2822 | fn 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 | |||
| 3967 | test "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#} |
| 3870 | const std = @import("std"); | 4013 | const std = @import("std"); |
| 3871 | const assert = std.debug.assert; | 4014 | const assert = std.debug.assert; |
| 3872 | const warn = std.debug.warn; | 4015 | const print = std.debug.print; |
| 3873 | 4016 | ||
| 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. |
| 3875 | fn deferExample() usize { | 4018 | fn 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 in | 4035 | // 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. |
| 3894 | fn deferUnwindExample() void { | 4037 | fn deferUnwindExample() void { |
| 3895 | warn("\n", .{}); | 4038 | print("\n", .{}); |
| 3896 | 4039 | ||
| 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 properly | 4061 | // 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. |
| 3920 | fn deferErrorExample(is_error: bool) !void { | 4063 | fn deferErrorExample(is_error: bool) !void { |
| 3921 | warn("\nstart of function\n", .{}); | 4064 | print("\nstart of function\n", .{}); |
| 3922 | 4065 | ||
| 3923 | // This will always be executed on exit | 4066 | // 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 | } |
| 3927 | 4070 | ||
| 3928 | errdefer { | 4071 | errdefer { |
| 3929 | warn("encountered an error!\n", .{}); | 4072 | print("encountered an error!\n", .{}); |
| 3930 | } | 4073 | } |
| 3931 | 4074 | ||
| 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#} |
| 4148 | const assert = @import("std").debug.assert; | 4291 | const assert = @import("std").debug.assert; |
| 4149 | 4292 | ||
| 4150 | fn addFortyTwo(x: var) @TypeOf(x) { | 4293 | fn addFortyTwo(x: anytype) @TypeOf(x) { |
| 4151 | return x + 42; | 4294 | return x + 42; |
| 4152 | } | 4295 | } |
| 4153 | 4296 | ||
| ... | @@ -5364,11 +5507,11 @@ const std = @import("std"); | ... | @@ -5364,11 +5507,11 @@ const std = @import("std"); |
| 5364 | const assert = std.debug.assert; | 5507 | const assert = std.debug.assert; |
| 5365 | 5508 | ||
| 5366 | test "turn HashMap into a set with void" { | 5509 | test "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(); |
| 5369 | 5512 | ||
| 5370 | _ = try map.put(1, {}); | 5513 | try map.put(1, {}); |
| 5371 | _ = try map.put(2, {}); | 5514 | try map.put(2, {}); |
| 5372 | 5515 | ||
| 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 | |||
| 5380 | fn hash_i32(x: i32) u32 { | ||
| 5381 | return @bitCast(u32, x); | ||
| 5382 | } | ||
| 5383 | |||
| 5384 | fn 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, and | 5524 | 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#} |
| 5928 | const warn = @import("std").debug.warn; | 6063 | const print = @import("std").debug.print; |
| 5929 | 6064 | ||
| 5930 | const a_number: i32 = 1234; | 6065 | const a_number: i32 = 1234; |
| 5931 | const a_string = "foobar"; | 6066 | const a_string = "foobar"; |
| 5932 | 6067 | ||
| 5933 | pub fn main() void { | 6068 | pub 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#} |
| 5937 | 6072 | ||
| ... | @@ -5941,7 +6076,7 @@ pub fn main() void { | ... | @@ -5941,7 +6076,7 @@ pub fn main() void { |
| 5941 | 6076 | ||
| 5942 | {#code_begin|syntax#} | 6077 | {#code_begin|syntax#} |
| 5943 | /// Calls print and then flushes the buffer. | 6078 | /// Calls print and then flushes the buffer. |
| 5944 | pub fn printf(self: *OutStream, comptime format: []const u8, args: var) anyerror!void { | 6079 | pub 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#} |
| 6030 | pub fn printValue(self: *OutStream, value: var) !void { | 6165 | pub 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#} |
| 6048 | const warn = @import("std").debug.warn; | 6183 | const print = @import("std").debug.print; |
| 6049 | 6184 | ||
| 6050 | const a_number: i32 = 1234; | 6185 | const a_number: i32 = 1234; |
| 6051 | const a_string = "foobar"; | 6186 | const a_string = "foobar"; |
| 6052 | 6187 | ||
| 6053 | test "printf too many arguments" { | 6188 | test "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#} |
| 6069 | const warn = @import("std").debug.warn; | 6204 | const print = @import("std").debug.print; |
| 6070 | 6205 | ||
| 6071 | const a_number: i32 = 1234; | 6206 | const a_number: i32 = 1234; |
| 6072 | const a_string = "foobar"; | 6207 | const a_string = "foobar"; |
| 6073 | const fmt = "here is a string: '{}' here is a number: {}\n"; | 6208 | const fmt = "here is a string: '{}' here is a number: {}\n"; |
| 6074 | 6209 | ||
| 6075 | pub fn main() void { | 6210 | pub 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 { |
| 6511 | 6646 | ||
| 6512 | fn amainWrap() void { | 6647 | fn 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); |
| 6543 | 6678 | ||
| 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 | } |
| 6547 | 6682 | ||
| 6548 | var global_download_frame: anyframe = undefined; | 6683 | var 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 | } |
| 6558 | 6693 | ||
| ... | @@ -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 { |
| 6581 | 6716 | ||
| 6582 | fn amainWrap() void { | 6717 | fn 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); |
| 6613 | 6748 | ||
| 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 | } |
| 6617 | 6752 | ||
| 6618 | fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 { | 6753 | fn 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 | } |
| 6624 | 6759 | ||
| 6625 | fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 { | 6760 | fn 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#} |
| 6691 | 6826 | ||
| 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#} |
| 6900 | 7035 | ||
| 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#} |
| 7124 | const warn = @import("std").debug.warn; | 7259 | const print = @import("std").debug.print; |
| 7125 | 7260 | ||
| 7126 | const num1 = blk: { | 7261 | const 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: { |
| 7133 | test "main" { | 7268 | test "main" { |
| 7134 | @compileLog("comptime in main"); | 7269 | @compileLog("comptime in main"); |
| 7135 | 7270 | ||
| 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#} |
| 7148 | const warn = @import("std").debug.warn; | 7283 | const print = @import("std").debug.print; |
| 7149 | 7284 | ||
| 7150 | const num1 = blk: { | 7285 | const 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 | }; |
| 7155 | 7290 | ||
| 7156 | test "main" { | 7291 | test "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#} |
| 7247 | 7382 | ||
| 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#} |
| 7282 | 7417 | ||
| 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#} |
| 7302 | 7437 | ||
| 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 error | 7441 | 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#} |
| 7310 | 7445 | ||
| 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#} |
| 7355 | 7490 | ||
| 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#} |
| 7389 | 7524 | ||
| 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 the | 7528 | 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#} |
| 7397 | 7532 | ||
| 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#} |
| 7423 | 7558 | ||
| 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#} |
| 7549 | 7684 | ||
| 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 in | 7689 | 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#} |
| 7590 | 7725 | ||
| 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#} |
| 7741 | 7876 | ||
| 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#} |
| 7752 | 7887 | ||
| 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#} |
| 8010 | 8145 | ||
| 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 value | 8149 | 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#} | ||
| 8175 | const std = @import("std"); | ||
| 8176 | const expect = std.testing.expect; | ||
| 8034 | 8177 | ||
| 8178 | test "@src" { | ||
| 8179 | doTheTest(); | ||
| 8180 | } | ||
| 8181 | |||
| 8182 | fn 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 instruction | 8195 | 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 instruction | 8206 | 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 instruction | 8217 | 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 instruction | 8228 | 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 instruction | 8239 | 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 instruction | 8250 | 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 instruction | 8261 | 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 instruction | 8272 | 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 instruction | 8283 | 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 instruction | 8327 | 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#} |
| 8187 | 8344 | ||
| 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#} |
| 8238 | 8395 | ||
| 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 smaller | 8399 | 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#} |
| 8381 | 8538 | ||
| 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"); |
| 8555 | pub fn main() void { | 8713 | pub 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"); |
| 8577 | pub fn main() void { | 8735 | pub 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"); |
| 8611 | pub fn main() void { | 8769 | pub 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#} |
| 8631 | const math = @import("std").math; | 8789 | const math = @import("std").math; |
| 8632 | const warn = @import("std").debug.warn; | 8790 | const print = @import("std").debug.print; |
| 8633 | pub fn main() !void { | 8791 | pub fn main() !void { |
| 8634 | var byte: u8 = 255; | 8792 | var byte: u8 = 255; |
| 8635 | 8793 | ||
| 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 | }; |
| 8640 | 8798 | ||
| 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#} |
| 8660 | const warn = @import("std").debug.warn; | 8818 | const print = @import("std").debug.print; |
| 8661 | pub fn main() void { | 8819 | pub fn main() void { |
| 8662 | var byte: u8 = 255; | 8820 | var byte: u8 = 255; |
| 8663 | 8821 | ||
| 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"); |
| 8710 | pub fn main() void { | 8868 | pub 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"); |
| 8728 | pub fn main() void { | 8886 | pub 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"); |
| 8810 | pub fn main() void { | 8968 | pub 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, with | 8974 | <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#} |
| 8819 | const warn = @import("std").debug.warn; | 8977 | const print = @import("std").debug.print; |
| 8820 | pub fn main() void { | 8978 | pub fn main() void { |
| 8821 | const optional_number: ?i32 = null; | 8979 | const optional_number: ?i32 = null; |
| 8822 | 8980 | ||
| 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"); |
| 8846 | 9004 | ||
| 8847 | pub fn main() void { | 9005 | pub 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 | } |
| 8851 | 9009 | ||
| 8852 | fn getNumberOrFail() !i32 { | 9010 | fn 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, with | 9014 | <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#} |
| 8859 | const warn = @import("std").debug.warn; | 9017 | const print = @import("std").debug.print; |
| 8860 | 9018 | ||
| 8861 | pub fn main() void { | 9019 | pub fn main() void { |
| 8862 | const result = getNumberOrFail(); | 9020 | const result = getNumberOrFail(); |
| 8863 | 9021 | ||
| 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 | } |
| 8870 | 9028 | ||
| ... | @@ -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 { |
| 8921 | pub fn main() void { | 9079 | pub 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 | } |
| 8959 | fn foo(set1: Set1) void { | 9117 | fn 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 { |
| 9015 | 9173 | ||
| 9016 | fn bar(f: *Foo) void { | 9174 | fn 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 { |
| 9039 | 9197 | ||
| 9040 | fn bar(f: *Foo) void { | 9198 | fn 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 | } |
| 9063 | 9221 | ||
| 9064 | fn bar(f: *Foo) void { | 9222 | fn 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; |
| 9179 | 9337 | ||
| 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. Everything | 9342 | 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); |
| 9713 | 9871 | ||
| 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(); |
| 9735 | 9893 | ||
| 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.wasm | 9899 | <pre><code>$ wasmtime --dir=. preopens.wasm |
| 9742 | 0: { .fd = 3, .Dir = '.' } | 9900 | 0: 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? VarDecl | 10316 | / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl |
| 10159 | / KEYWORD_usingnamespace Expr SEMICOLON | 10317 | / KEYWORD_usingnamespace Expr SEMICOLON |
| 10160 | 10318 | ||
| 10161 | FnProto &lt;- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr) | 10319 | FnProto &lt;- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype / TypeExpr) |
| 10162 | 10320 | ||
| 10163 | VarDecl &lt;- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON | 10321 | VarDecl &lt;- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON |
| 10164 | 10322 | ||
| ... | @@ -10330,7 +10488,7 @@ LinkSection &lt;- KEYWORD_linksection LPAREN Expr RPAREN | ... | @@ -10330,7 +10488,7 @@ LinkSection &lt;- KEYWORD_linksection LPAREN Expr RPAREN |
| 10330 | ParamDecl &lt;- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType | 10488 | ParamDecl &lt;- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType |
| 10331 | 10489 | ||
| 10332 | ParamType | 10490 | ParamType |
| 10333 | &lt;- KEYWORD_var | 10491 | &lt;- KEYWORD_anytype |
| 10334 | / DOT3 | 10492 | / DOT3 |
| 10335 | / TypeExpr | 10493 | / TypeExpr |
| 10336 | 10494 | ||
| ... | @@ -10568,6 +10726,7 @@ KEYWORD_align &lt;- 'align' end_of_word | ... | @@ -10568,6 +10726,7 @@ KEYWORD_align &lt;- 'align' end_of_word |
| 10568 | KEYWORD_allowzero &lt;- 'allowzero' end_of_word | 10726 | KEYWORD_allowzero &lt;- 'allowzero' end_of_word |
| 10569 | KEYWORD_and &lt;- 'and' end_of_word | 10727 | KEYWORD_and &lt;- 'and' end_of_word |
| 10570 | KEYWORD_anyframe &lt;- 'anyframe' end_of_word | 10728 | KEYWORD_anyframe &lt;- 'anyframe' end_of_word |
| 10729 | KEYWORD_anytype &lt;- 'anytype' end_of_word | ||
| 10571 | KEYWORD_asm &lt;- 'asm' end_of_word | 10730 | KEYWORD_asm &lt;- 'asm' end_of_word |
| 10572 | KEYWORD_async &lt;- 'async' end_of_word | 10731 | KEYWORD_async &lt;- 'async' end_of_word |
| 10573 | KEYWORD_await &lt;- 'await' end_of_word | 10732 | KEYWORD_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 |
| 10613 | KEYWORD_volatile &lt;- 'volatile' end_of_word | 10772 | KEYWORD_volatile &lt;- 'volatile' end_of_word |
| 10614 | KEYWORD_while &lt;- 'while' end_of_word | 10773 | KEYWORD_while &lt;- 'while' end_of_word |
| 10615 | 10774 | ||
| 10616 | keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_allowzero / KEYWORD_asm | 10775 | keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_anyframe / KEYWORD_anytype |
| 10617 | / KEYWORD_async / KEYWORD_await / KEYWORD_break | 10776 | / KEYWORD_allowzero / KEYWORD_asm / KEYWORD_async / KEYWORD_await / KEYWORD_break |
| 10618 | / KEYWORD_catch / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue | 10777 | / KEYWORD_catch / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue |
| 10619 | / KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer | 10778 | / KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer |
| 10620 | / KEYWORD_error / KEYWORD_export / KEYWORD_extern / KEYWORD_false | 10779 | / KEYWORD_error / KEYWORD_export / KEYWORD_extern / KEYWORD_false |
| 10621 | / KEYWORD_fn / KEYWORD_for / KEYWORD_if / KEYWORD_inline | 10780 | / KEYWORD_fn / KEYWORD_for / KEYWORD_if / KEYWORD_inline |
| 10622 | / KEYWORD_noalias / KEYWORD_null / KEYWORD_or | 10781 | / KEYWORD_noalias / KEYWORD_null / KEYWORD_or |
| 10623 | / KEYWORD_orelse / KEYWORD_packed / KEYWORD_anyframe / KEYWORD_pub | 10782 | / KEYWORD_orelse / KEYWORD_packed / KEYWORD_pub |
| 10624 | / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection | 10783 | / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection |
| 10625 | / KEYWORD_struct / KEYWORD_suspend | 10784 | / KEYWORD_struct / KEYWORD_suspend |
| 10626 | / KEYWORD_switch / KEYWORD_test / KEYWORD_threadlocal / KEYWORD_true / KEYWORD_try | 10785 | / 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 list | 54 | /// 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 | } |
| 59 | 59 | ||
| ... | @@ -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 | } |
| 164 | 164 | ||
| 165 | /// Same as `append` except it returns the number of bytes written, which is always the same | 165 | 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 | } | ||
| 172 | 167 | ||
| 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 | }; | ||
| 178 | 183 | ||
| 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 | } |
| 207 | 212 | ||
| 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 | } |
| 216 | 229 | ||
| 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 | } |
| 246 | 259 | ||
| 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 | } |
| 429 | 460 | ||
| 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 | } |
| 438 | 477 | ||
| 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 | } |
| 469 | 508 | ||
| 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 | |||
| 755 | test "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 | |||
| 767 | test "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 | } |
| 70 | 70 | ||
| 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 | } |
| 84 | 84 | ||
| 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 | } |
| 88 | 88 |
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 is | 124 | /// 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 | } |
| 41 | 41 | ||
| 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 | } |
| 57 | 57 | ||
| 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 | } |
| 62 | 61 | ||
| 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 | } |
| 80 | 79 | ||
| 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 | }; |
| 85 | 84 |
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 | } |
| 16 | 16 | ||
| 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 | } |
| 26 | 24 | ||
| 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 | } |
| 287 | 287 | ||
| 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 | } |
| 291 | 291 | ||
| 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 | } |
| 314 | 314 | ||
| 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; |
| 429 | 429 | ||
| 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; |
| 524 | 524 | ||
| 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; |
| 528 | 528 | ||
| 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.ReleaseSafe | 530 | 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; |
| 560 | 560 | ||
| 561 | // TODO add cpu and features as part of the target triple | 561 | // 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 | } |
| 644 | 644 | ||
| 645 | // option already exists | 645 | // 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 list | 648 | // 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 | } |
| 685 | 685 | ||
| 686 | // option already exists | 686 | // 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 | } |
| 885 | 885 | ||
| 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 | } |
| 889 | 889 | ||
| ... | @@ -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 | } |
| 1914 | 1915 | ||
| ... | @@ -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 | |||
| 2563 | test "" { | ||
| 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 | } |
| 128 | 128 | ||
| 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 and | 130 | 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 | |||
| 219 | test "" { | ||
| 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 | }; |
| 133 | 133 | ||
| 134 | /// This data structure is used by the Zig language code generation and | ||
| 135 | /// therefore must be kept in sync with the compiler implementation. | ||
| 136 | pub const SourceLocation = struct { | ||
| 137 | file: [:0]const u8, | ||
| 138 | fn_name: [:0]const u8, | ||
| 139 | line: u32, | ||
| 140 | column: u32, | ||
| 141 | }; | ||
| 142 | |||
| 134 | pub const TypeId = @TagType(TypeInfo); | 143 | pub const TypeId = @TagType(TypeInfo); |
| 135 | 144 | ||
| 136 | /// This data structure is used by the Zig language code generation and | 145 | /// 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 is | 198 | /// 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 way | 199 | /// 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, |
| 193 | 202 | ||
| 194 | /// This data structure is used by the Zig language code generation and | 203 | /// 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 is | 220 | /// 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 way | 221 | /// 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 | }; |
| 216 | 225 | ||
| 217 | /// This data structure is used by the Zig language code generation and | 226 | /// 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 | }; |
| 233 | 242 | ||
| 234 | /// This data structure is used by the Zig language code generation and | 243 | /// 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 | }; |
| 241 | 251 | ||
| 242 | /// This data structure is used by the Zig language code generation and | 252 | /// 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 | }; |
| 261 | 272 | ||
| 262 | /// This data structure is used by the Zig language code generation and | 273 | /// 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; |
| 265 | 276 | ||
| 266 | /// This data structure is used by the Zig language code generation and | 277 | /// 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 | }; |
| 282 | 293 | ||
| ... | @@ -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 | }; |
| 299 | 310 | ||
| 300 | /// This data structure is used by the Zig language code generation and | 311 | /// 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 | }; |
| 317 | 334 | ||
| 318 | /// This data structure is used by the Zig language code generation and | 335 | /// 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, |
| 356 | 373 | ||
| 357 | /// This data structure is used by the Zig language code generation and | 374 | /// 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 | }; |
| 29 | 29 | ||
| 30 | pub fn getErrno(rc: var) u16 { | 30 | pub 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; |
| 73 | pub extern "c" fn exit(code: c_int) noreturn; | 73 | pub extern "c" fn exit(code: c_int) noreturn; |
| 74 | pub extern "c" fn isatty(fd: fd_t) c_int; | 74 | pub extern "c" fn isatty(fd: fd_t) c_int; |
| 75 | pub extern "c" fn close(fd: fd_t) c_int; | 75 | pub extern "c" fn close(fd: fd_t) c_int; |
| 76 | pub extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, stat_buf: *Stat, flags: u32) c_int; | ||
| 77 | pub extern "c" fn lseek(fd: fd_t, offset: off_t, whence: c_int) off_t; | 76 | pub extern "c" fn lseek(fd: fd_t, offset: off_t, whence: c_int) off_t; |
| 78 | pub extern "c" fn open(path: [*:0]const u8, oflag: c_uint, ...) c_int; | 77 | pub extern "c" fn open(path: [*:0]const u8, oflag: c_uint, ...) c_int; |
| 79 | pub extern "c" fn openat(fd: c_int, path: [*:0]const u8, oflag: c_uint, ...) c_int; | 78 | pub 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; |
| 102 | pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int; | 101 | pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int; |
| 103 | pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int; | 102 | pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int; |
| 104 | pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int; | 103 | pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int; |
| 104 | pub extern "c" fn symlinkat(oldpath: [*:0]const u8, newdirfd: fd_t, newpath: [*:0]const u8) c_int; | ||
| 105 | pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int; | 105 | pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int; |
| 106 | pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int; | 106 | pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int; |
| 107 | pub extern "c" fn chdir(path: [*:0]const u8) c_int; | 107 | pub 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 |
| 115 | pub usingnamespace switch (builtin.os.tag) { | 115 | pub 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 | }; |
| 123 | 125 | ||
| ... | @@ -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; |
| 231 | 233 | ||
| 232 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; | 234 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; |
| 233 | pub extern "c" fn malloc(usize) ?*c_void; | 235 | pub extern "c" fn malloc(usize) ?*c_void; |
| 236 | |||
| 237 | pub 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 | |||
| 234 | pub extern "c" fn realloc(?*c_void, usize) ?*c_void; | 247 | pub extern "c" fn realloc(?*c_void, usize) ?*c_void; |
| 235 | pub extern "c" fn free(*c_void) void; | 248 | pub extern "c" fn free(*c_void) void; |
| 236 | pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; | 249 | pub 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"), |
| 66 | 66 | ||
| 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), |
| 116 | 116 | ||
| 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, |
| 131 | 131 | ||
| 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, |
| 143 | 143 | ||
| 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, |
| 152 | 152 | ||
| 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) { |
| 163 | 163 | ||
| 164 | token: TokenIndex, | 164 | token: TokenIndex, |
| 165 | 165 | ||
| 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 | }, |
| 319 | 319 | ||
| 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 |
| 16 | 16 | ||
| 17 | pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize; | 17 | pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize; |
| 18 | pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int; | 18 | pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int; |
| 19 | pub extern "c" fn @"fstatat$INODE64"(dirfd: fd_t, path_name: [*:0]const u8, buf: *Stat, flags: u32) c_int; | ||
| 19 | 20 | ||
| 20 | pub extern "c" fn mach_absolute_time() u64; | 21 | pub extern "c" fn mach_absolute_time() u64; |
| 21 | pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void; | 22 | pub 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 { |
| 278 | 278 | ||
| 279 | // TODO extensions | 279 | // 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 }, |
| 313 | 313 | ||
| 314 | // ISO C99 | 314 | // 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 }, |
| 320 | 320 | ||
| 321 | // ISO C11 | 321 | // 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 }, |
| 329 | 329 | ||
| 330 | // Preprocessor directives | 330 | // 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 | }); |
| 338 | 338 | ||
| 339 | // TODO do this in the preprocessor | 339 | // 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 { |
| 70 | 70 | ||
| 71 | /// Convert the input value into bytes and record it as a dependency of the | 71 | /// Convert the input value into bytes and record it as a dependency of the |
| 72 | /// process being cached | 72 | /// 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); |
| 75 | 75 | ||
| 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 | } |
| 208 | 208 | ||
| 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 | } |
| 212 | 212 | ||
| 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 structs | 8 | /// `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/4335 | 10 | /// TODO: https://github.com/ziglang/zig/issues/4335 |
| 11 | pub fn ComptimeStringMap(comptime V: type, comptime kvs: var) type { | 11 | pub 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 | } |
| 128 | 128 | ||
| 129 | fn testMap(comptime map: var) void { | 129 | fn 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 | } |
| 167 | 167 | ||
| 168 | fn testSet(comptime map: var) void { | 168 | fn 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 | }; |
| 31 | 31 | ||
| 32 | pub fn benchmarkHash(comptime Hash: var, comptime bytes: comptime_int) !u64 { | 32 | pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 { |
| 33 | var h = Hash.init(); | 33 | var h = Hash.init(); |
| 34 | 34 | ||
| 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 | }; |
| 58 | 58 | ||
| 59 | pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 { | 59 | pub 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); |
| 61 | 61 | ||
| 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 { |
| 81 | 81 | ||
| 82 | const exchanges = [_]Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }}; | 82 | const exchanges = [_]Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }}; |
| 83 | 83 | ||
| 84 | pub fn benchmarkKeyExchange(comptime DhKeyExchange: var, comptime exchange_count: comptime_int) !u64 { | 84 | pub 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); |
| 86 | 86 | ||
| 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 | } |
| 125 | 125 | ||
| 126 | // TODO(#1358): Replace with builtin formatted padding when available. | ||
| 127 | fn 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 | |||
| 135 | pub fn main() !void { | 126 | pub fn main() !void { |
| 136 | const stdout = std.io.getStdOut().outStream(); | 127 | const stdout = std.io.getStdOut().outStream(); |
| 137 | 128 | ||
| ... | @@ -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 | } |
| 182 | 172 | ||
| 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 | } |
| 190 | 179 | ||
| 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; |
| 4 | const fmt = std.fmt; | 4 | const fmt = std.fmt; |
| 5 | 5 | ||
| 6 | // Hash using the specified hasher `H` asserting `expected == H(input)`. | 6 | // Hash using the specified hasher `H` asserting `expected == H(input)`. |
| 7 | pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, input: []const u8) void { | 7 | pub 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..]); |
| 10 | 10 |
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 | }; |
| 52 | 52 | ||
| 53 | /// Tries to write to stderr, unbuffered, and ignores any error returned. | ||
| 54 | /// Does not append a newline. | ||
| 55 | var stderr_file: File = undefined; | ||
| 56 | var stderr_file_writer: File.Writer = undefined; | ||
| 57 | |||
| 58 | var stderr_stream: ?*File.OutStream = null; | ||
| 59 | var stderr_mutex = std.Mutex.init(); | 53 | var stderr_mutex = std.Mutex.init(); |
| 60 | 54 | ||
| 61 | pub 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". | ||
| 57 | pub 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. | ||
| 61 | pub 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 | } |
| 67 | 67 | ||
| 68 | pub 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 | |||
| 80 | pub fn getStderrMutex() *std.Mutex { | 68 | pub 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 awareness | 102 | /// TODO multithreaded awareness |
| 114 | pub fn dumpCurrentStackTrace(start_addr: ?usize) void { | 103 | pub 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 awareness | 123 | /// TODO multithreaded awareness |
| 135 | pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { | 124 | pub 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 awareness | 193 | /// TODO multithreaded awareness |
| 205 | pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { | 194 | pub 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 failure | 223 | if (!ok) unreachable; // assertion failure |
| 235 | } | 224 | } |
| 236 | 225 | ||
| 237 | pub fn panic(comptime format: []const u8, args: var) noreturn { | 226 | pub 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_address | 228 | // 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. |
| 253 | threadlocal var panic_stage: usize = 0; | 242 | threadlocal var panic_stage: usize = 0; |
| 254 | 243 | ||
| 255 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn { | 244 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: anytype) noreturn { |
| 256 | @setCold(true); | 245 | @setCold(true); |
| 257 | 246 | ||
| 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(); |
| 274 | 263 | ||
| 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 to | 287 | // 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"; |
| 317 | 306 | ||
| 318 | pub fn writeStackTrace( | 307 | pub 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 | }; |
| 396 | 385 | ||
| 397 | pub fn writeCurrentStackTrace( | 386 | pub 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 | } |
| 411 | 400 | ||
| 412 | pub fn writeCurrentStackTraceWindows( | 401 | pub 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 handle | 435 | // TODO give this a payload of file handle |
| 447 | windows_api, | 436 | windows_api, |
| 448 | 437 | ||
| 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 | } |
| 566 | 556 | ||
| 567 | /// TODO resources https://github.com/ziglang/zig/issues/4353 | 557 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 568 | pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void { | 558 | pub 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 | } |
| 597 | 587 | ||
| 598 | fn printLineInfo( | 588 | fn 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 | } |
| 832 | 822 | ||
| 833 | fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { | 823 | fn 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 | } |
| 1016 | 1006 | ||
| 1017 | fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { | 1007 | fn printLineFromFileAnyOs(out_stream: anytype, line_info: LineInfo) !void { |
| 1018 | // Need this to always block even in async I/O mode, because this could potentially | 1008 | // 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; |
| 1143 | 1133 | ||
| 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 | } |
| 1148 | 1138 | ||
| ... | @@ -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; |
| 1215 | 1205 | ||
| 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 | } |
| 1220 | 1210 | ||
| ... | @@ -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 | } |
| 1290 | 1280 | ||
| 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 | } |
| 1294 | 1284 | ||
| ... | @@ -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..]); |
| 1452 | 1442 | ||
| 1453 | // Check if its debug infos are already in the cache | 1443 | // Check if its debug infos are already in the cache |
| 1454 | var o_file_di = self.ofiles.getValue(o_file_path) orelse | 1444 | 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 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | 3 | ||
| 4 | pub 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. |
| 6 | pub 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); | ||
| 6 | 9 | ||
| 7 | var result: T = 0; | 10 | const max_group = (U.bit_count + 6) / 7; |
| 8 | var shift: usize = 0; | ||
| 9 | 11 | ||
| 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; | ||
| 19 | 14 | ||
| 20 | result |= operand; | 15 | while (group < max_group) : (group += 1) { |
| 16 | const byte = try reader.readByte(); | ||
| 17 | var temp = @as(U, byte & 0x7f); | ||
| 21 | 18 | ||
| 22 | if ((byte & 0x80) == 0) | 19 | if (@shlWithOverflow(U, temp, group * 7, &temp)) return error.Overflow; |
| 23 | return result; | ||
| 24 | 20 | ||
| 25 | shift += 7; | 21 | value |= temp; |
| 22 | if (byte & 0x80 == 0) break; | ||
| 23 | } else { | ||
| 24 | return error.Overflow; | ||
| 26 | } | 25 | } |
| 27 | } | ||
| 28 | |||
| 29 | pub fn readULEB128Mem(comptime T: type, ptr: *[*]const u8) !T { | ||
| 30 | const ShiftT = std.meta.Int(false, std.math.log2(T.bit_count)); | ||
| 31 | 26 | ||
| 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; |
| 35 | 30 | } | |
| 36 | while (true) : (i += 1) { | ||
| 37 | const byte = ptr.*[i]; | ||
| 38 | |||
| 39 | if (shift > T.bit_count) | ||
| 40 | return error.Overflow; | ||
| 41 | 31 | ||
| 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; | ||
| 45 | 34 | ||
| 46 | result |= operand; | 35 | /// Write a single unsigned integer as unsigned LEB128 to the given writer. |
| 36 | pub 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); | ||
| 47 | 40 | ||
| 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 | } |
| 56 | 52 | ||
| 57 | pub 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)); | 55 | pub 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 | } | ||
| 60 | 61 | ||
| 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. |
| 64 | pub 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 | } | ||
| 63 | 71 | ||
| 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. |
| 74 | pub 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); | ||
| 66 | 78 | ||
| 67 | if (shift > T.bit_count) | 79 | const max_group = (U.bit_count + 6) / 7; |
| 68 | return error.Overflow; | ||
| 69 | 80 | ||
| 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 | } | ||
| 75 | 83 | ||
| 76 | result |= operand; | 84 | while (group < max_group) : (group += 1) { |
| 85 | const byte = try reader.readByte(); | ||
| 86 | var temp = @as(U, byte & 0x7f); | ||
| 77 | 87 | ||
| 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; | ||
| 79 | 93 | ||
| 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 | } | ||
| 88 | 112 | ||
| 89 | pub 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 | } | ||
| 92 | 118 | ||
| 93 | var result: UT = 0; | 119 | return @truncate(T, result); |
| 94 | var shift: usize = 0; | 120 | } |
| 95 | var i: usize = 0; | ||
| 96 | 121 | ||
| 97 | while (true) : (i += 1) { | 122 | /// Write a single signed integer as signed LEB128 to the given writer. |
| 98 | const byte = ptr.*[i]; | 123 | pub 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); | ||
| 99 | 127 | ||
| 100 | if (shift > T.bit_count) | 128 | var value = @intCast(S, int_value); |
| 101 | return error.Overflow; | ||
| 102 | 129 | ||
| 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 | } | ||
| 108 | 143 | ||
| 109 | result |= operand; | 144 | /// Read a single singed LEB128 integer from the given memory as type T. |
| 110 | 145 | /// The provided slice reference will be updated to point to the byte after the last byte read. | |
| 111 | shift += 7; | 146 | pub 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 | } | ||
| 112 | 152 | ||
| 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); | 155 | pub 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 | } |
| 122 | 161 | ||
| 162 | // tests | ||
| 123 | fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T { | 163 | fn 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 | } |
| 127 | 167 | ||
| 128 | fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T { | 168 | fn 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 | } |
| 132 | 172 | ||
| 133 | fn test_read_ileb128(comptime T: type, encoded: []const u8) !T { | 173 | fn 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 | } |
| 141 | 181 | ||
| 142 | fn test_read_uleb128(comptime T: type, encoded: []const u8) !T { | 182 | fn 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 | } |
| 150 | 190 | ||
| 151 | fn test_read_ileb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void { | 191 | fn 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 | } |
| 161 | 201 | ||
| 162 | fn test_read_uleb128_seq(comptime T: type, comptime N: usize, encoded: []const u8) void { | 202 | fn 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); |
| 213 | 253 | ||
| 214 | // Decode sequence of SLEB128 values | 254 | // 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 | } |
| 217 | 257 | ||
| 218 | test "deserialize unsigned LEB128" { | 258 | test "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); |
| 253 | 293 | ||
| 254 | // Decode sequence of ULEB128 values | 294 | // 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 | |||
| 298 | fn 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 | |||
| 360 | test "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 | |||
| 374 | test "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 | }; |
| 238 | 238 | ||
| 239 | fn readUnitLength(in_stream: var, endian: builtin.Endian, is_64: *bool) !u64 { | 239 | fn 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 | } |
| 250 | 250 | ||
| 251 | // TODO the nosuspends here are workarounds | 251 | // TODO the nosuspends here are workarounds |
| 252 | fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { | 252 | fn 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 | } |
| 258 | 258 | ||
| 259 | // TODO the nosuspends here are workarounds | 259 | // TODO the nosuspends here are workarounds |
| 260 | fn readAddress(in_stream: var, endian: builtin.Endian, is_64: bool) !u64 { | 260 | fn 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 | else | 263 | else |
| 264 | @as(u64, try in_stream.readInt(u32, endian)); | 264 | @as(u64, try in_stream.readInt(u32, endian)); |
| 265 | } | 265 | } |
| 266 | 266 | ||
| 267 | fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { | 267 | fn 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 | } |
| 271 | 271 | ||
| 272 | // TODO the nosuspends here are workarounds | 272 | // TODO the nosuspends here are workarounds |
| 273 | fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, endian: builtin.Endian, size: usize) !FormValue { | 273 | fn 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 | } |
| 277 | 277 | ||
| 278 | fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, endian: builtin.Endian, comptime size: i32) !FormValue { | 278 | fn 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 | } |
| 303 | 303 | ||
| 304 | // TODO the nosuspends here are workarounds | 304 | // TODO the nosuspends here are workarounds |
| 305 | fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, endian: builtin.Endian, size: i32) !FormValue { | 305 | fn 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 | } |
| 317 | 317 | ||
| 318 | // TODO the nosuspends here are workarounds | 318 | // TODO the nosuspends here are workarounds |
| 319 | fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, endian: builtin.Endian, is_64: bool) anyerror!FormValue { | 319 | fn 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 | } |
| 672 | 672 | ||
| 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 | } |
| 519 | 519 | ||
| 520 | pub fn int(is_64: bool, need_bswap: bool, int_32: var, int_64: var) @TypeOf(int_64) { | 520 | pub 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 | } |
| 531 | 531 | ||
| 532 | pub fn int32(need_bswap: bool, int_32: var, comptime Int64: var) Int64 { | 532 | pub 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 notation | 64 | /// - `e`: output floating point value in scientific notation |
| 65 | /// - `d`: output numeric value in decimal notation | 65 | /// - `d`: output numeric value in decimal notation |
| 66 | /// - `b`: output integer value in binary notation | 66 | /// - `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 type | 71 | /// 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) !void | 73 | /// 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. |
| 78 | pub fn format( | 79 | pub 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 | } |
| 141 | 142 | ||
| 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 | ); |
| 189 | 190 | ||
| ... | @@ -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 | } |
| 312 | 313 | ||
| 313 | pub fn formatType( | 314 | pub 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 | } |
| 326 | 327 | ||
| 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 | } |
| 331 | 332 | ||
| 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 | } |
| 367 | 368 | ||
| 368 | // Use @tagName only if value is one of known fields | 369 | // 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 | } |
| 376 | 378 | ||
| 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 | } |
| 490 | 492 | ||
| 491 | fn formatValue( | 493 | fn 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 | } |
| 502 | 504 | ||
| 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 | } |
| 511 | 513 | ||
| 512 | pub fn formatIntValue( | 514 | pub 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 | } |
| 548 | 553 | ||
| 549 | return formatInt(int_value, radix, uppercase, options, out_stream); | 554 | return formatInt(int_value, radix, uppercase, options, writer); |
| 550 | } | 555 | } |
| 551 | 556 | ||
| 552 | fn formatFloatValue( | 557 | fn 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( |
| 585 | pub fn formatAsciiChar( | 590 | pub 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 | } |
| 592 | 597 | ||
| 593 | pub fn formatBuf( | 598 | pub 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 the | 630 | // 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. |
| 627 | pub fn formatFloatScientific( | 632 | pub 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); |
| 633 | 638 | ||
| 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 | } |
| 639 | 644 | ||
| 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"); |
| 648 | 653 | ||
| 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 | } |
| 660 | 665 | ||
| 661 | try out_stream.writeAll("e+00"); | 666 | try writer.writeAll("e+00"); |
| 662 | return; | 667 | return; |
| 663 | } | 668 | } |
| 664 | 669 | ||
| ... | @@ -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); |
| 670 | 675 | ||
| 671 | try out_stream.writeAll(float_decimal.digits[0..1]); | 676 | try writer.writeAll(float_decimal.digits[0..1]); |
| 672 | 677 | ||
| 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("."); |
| 676 | 681 | ||
| 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 | } |
| 683 | 688 | ||
| 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; |
| 693 | 698 | ||
| 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 | } |
| 699 | 704 | ||
| 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; |
| 702 | 707 | ||
| 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 | } |
| 717 | 722 | ||
| 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). |
| 720 | pub fn formatFloatDecimal( | 725 | pub 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); |
| 726 | 731 | ||
| 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 | } |
| 732 | 737 | ||
| 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"); |
| 741 | 746 | ||
| 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 | } |
| 753 | 758 | ||
| ... | @@ -769,14 +774,14 @@ pub fn formatFloatDecimal( | ... | @@ -769,14 +774,14 @@ pub fn formatFloatDecimal( |
| 769 | 774 | ||
| 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]); |
| 773 | 778 | ||
| 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 | } |
| 781 | 786 | ||
| 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 | } |
| 786 | 791 | ||
| 787 | try out_stream.writeAll("."); | 792 | try writer.writeAll("."); |
| 788 | 793 | ||
| 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( |
| 796 | 801 | ||
| 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 | } |
| 802 | 807 | ||
| ... | @@ -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; |
| 816 | 821 | ||
| 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( |
| 827 | 832 | ||
| 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]); |
| 831 | 836 | ||
| 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 | } |
| 839 | 844 | ||
| 840 | // Omit `.` if no fractional portion | 845 | // 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 | } |
| 844 | 849 | ||
| 845 | try out_stream.writeAll("."); | 850 | try writer.writeAll("."); |
| 846 | 851 | ||
| 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( |
| 850 | 855 | ||
| 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 | } |
| 856 | 861 | ||
| 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 | } |
| 860 | 865 | ||
| 861 | pub fn formatBytes( | 866 | pub 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 | } |
| 870 | 875 | ||
| 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 | }; |
| 887 | 892 | ||
| 888 | try formatFloatDecimal(new_value, options, out_stream); | 893 | try formatFloatDecimal(new_value, options, writer); |
| 889 | 894 | ||
| 890 | if (suffix == ' ') { | 895 | if (suffix == ' ') { |
| 891 | return out_stream.writeAll("B"); | 896 | return writer.writeAll("B"); |
| 892 | } | 897 | } |
| 893 | 898 | ||
| 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 | } |
| 901 | 906 | ||
| 902 | pub fn formatInt( | 907 | pub 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; |
| 914 | 919 | ||
| 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 | } |
| 921 | 926 | ||
| 922 | fn formatIntSigned( | 927 | fn 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 | } |
| 948 | 953 | ||
| 949 | fn formatIntUnsigned( | 954 | fn 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 | } |
| 991 | 996 | ||
| 992 | pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize { | 997 | pub 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 | } |
| 997 | 1002 | ||
| 998 | pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { | 1003 | pub 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 | |||
| 1010 | test "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 | |||
| 1020 | pub const ParseUnsignedError = error{ | ||
| 1021 | /// The result cannot fit in the type specified | 1004 | /// The result cannot fit in the type specified |
| 1022 | Overflow, | 1005 | Overflow, |
| 1023 | 1006 | ||
| 1024 | /// The input had a byte that was not a digit | 1007 | /// The input was empty or had a byte that was not a digit |
| 1025 | InvalidCharacter, | 1008 | InvalidCharacter, |
| 1026 | }; | 1009 | }; |
| 1027 | 1010 | ||
| 1028 | pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T { | 1011 | pub 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 | |||
| 1018 | test "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 | |||
| 1045 | fn 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; |
| 1030 | 1059 | ||
| 1031 | for (buf) |c| { | 1060 | for (buf) |c| { |
| 1032 | const digit = try charToDigit(c, radix); | 1061 | const digit = try charToDigit(c, radix); |
| 1033 | 1062 | ||
| 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 | } |
| 1037 | 1066 | ||
| 1038 | return x; | 1067 | return x; |
| 1039 | } | 1068 | } |
| 1040 | 1069 | ||
| 1070 | pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseIntError!T { | ||
| 1071 | return parseWithSign(T, buf, radix, .Pos); | ||
| 1072 | } | ||
| 1073 | |||
| 1041 | test "parseUnsigned" { | 1074 | test "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 | } |
| 1067 | 1107 | ||
| 1068 | pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; | 1108 | pub 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 | }; |
| 1099 | pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 { | 1139 | pub 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 | } |
| 1104 | 1144 | ||
| 1105 | // Count the characters needed for format. Useful for preallocating memory | 1145 | // Count the characters needed for format. Useful for preallocating memory |
| 1106 | pub fn count(comptime fmt: []const u8, args: var) u64 { | 1146 | pub 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 | } |
| 1111 | 1151 | ||
| 1112 | pub const AllocPrintError = error{OutOfMemory}; | 1152 | pub const AllocPrintError = error{OutOfMemory}; |
| 1113 | 1153 | ||
| 1114 | pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![]u8 { | 1154 | pub 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 | } |
| 1124 | 1164 | ||
| 1125 | pub fn allocPrint0(allocator: *mem.Allocator, comptime fmt: []const u8, args: var) AllocPrintError![:0]u8 { | 1165 | pub 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 | } |
| 1150 | 1190 | ||
| 1151 | fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) []u8 { | 1191 | fn 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 | } |
| 1154 | 1194 | ||
| ... | @@ -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 | } |
| 1208 | 1252 | ||
| 1209 | test "int.padded" { | 1253 | test "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")); |
| 1220 | 1264 | ||
| 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")); |
| 1224 | 1268 | ||
| 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 | } |
| 1325 | 1372 | ||
| 1326 | test "non-exhaustive enum" { | 1373 | test "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 | } |
| 1536 | 1583 | ||
| 1537 | fn testFmt(expected: []const u8, comptime template: []const u8, args: var) !void { | 1584 | fn 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" { |
| 1604 | 1651 | ||
| 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 | } |
| 1610 | 1657 | ||
| ... | @@ -1613,7 +1660,7 @@ test "formatFloatValue with comptime_float" { | ... | @@ -1613,7 +1660,7 @@ test "formatFloatValue with comptime_float" { |
| 1613 | 1660 | ||
| 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")); |
| 1618 | 1665 | ||
| 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" { |
| 1669 | 1716 | ||
| 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{ ... }")); |
| 1674 | 1721 | ||
| 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) }")); |
| 1678 | 1725 | ||
| 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) }")); |
| 1682 | 1729 | ||
| 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 | } |
| 1687 | 1734 |
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, |
| 263 | 263 | ||
| 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 | }; |
| 276 | 266 | ||
| 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 { |
| 463 | 453 | ||
| 464 | pub const Error = IteratorError; | 454 | pub const Error = IteratorError; |
| 465 | 455 | ||
| 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]); |
| 558 | 551 | ||
| ... | @@ -566,12 +559,12 @@ pub const Dir = struct { | ... | @@ -566,12 +559,12 @@ pub const Dir = struct { |
| 566 | } | 559 | } |
| 567 | 560 | ||
| 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_REMOVEDIR | 1117 | 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 | } |
| 1127 | 1129 | ||
| 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_REMOVEDIR | 1135 | 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(); |
| 1231 | 1244 | ||
| 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); | ||
| 1237 | 1246 | ||
| 1238 | try file.inStream().readNoEof(buf); | 1247 | return file.readAllAllocOptions(allocator, stat_size, max_bytes, alignment, optional_sentinel); |
| 1239 | return buf; | ||
| 1240 | } | 1248 | } |
| 1241 | 1249 | ||
| 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 { |
| 1532 | 1540 | ||
| 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 | }; |
| 1539 | 1547 | ||
| 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 | }; |
| 1564 | 1583 | ||
| 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 get | 1827 | // 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 | } |
| 1813 | 1832 | ||
| 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 get | 1890 | // 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 | } |
| 1876 | 1895 | ||
| 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_MAX | 1912 | // 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 | } |
| 1898 | 1917 | ||
| 1899 | test "" { | 1918 | test "" { |
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; |
| 31 | 31 | ||
| 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/O | 221 | /// 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 what | 234 | /// 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, | ||
| 229 | 242 | ||
| 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 | } |
| 308 | 343 | ||
| 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; |
| 311 | 373 |
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 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const testing = std.testing; | ||
| 2 | const builtin = std.builtin; | 3 | const builtin = std.builtin; |
| 3 | const fs = std.fs; | 4 | const fs = std.fs; |
| 5 | const mem = std.mem; | ||
| 6 | const wasi = std.os.wasi; | ||
| 7 | |||
| 8 | const ArenaAllocator = std.heap.ArenaAllocator; | ||
| 9 | const Dir = std.fs.Dir; | ||
| 4 | const File = std.fs.File; | 10 | const File = std.fs.File; |
| 11 | const tmpDir = testing.tmpDir; | ||
| 12 | |||
| 13 | test "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 | |||
| 42 | fn 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 | |||
| 46 | fn 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 | |||
| 53 | test "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 | |||
| 86 | test "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 | |||
| 119 | test "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 | } | ||
| 5 | 155 | ||
| 6 | test "openSelfExe" { | 156 | test "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 | } |
| 12 | 162 | ||
| 163 | test "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 | |||
| 178 | test "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 | |||
| 196 | test "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 | |||
| 261 | test "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 | |||
| 283 | fn 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 | |||
| 290 | test "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 | |||
| 313 | test "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 | |||
| 13 | const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.ns_per_ms; | 320 | const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.ns_per_ms; |
| 14 | 321 | ||
| 15 | test "open file with exclusive nonblocking lock twice" { | 322 | test "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" { |
| 116 | test "open file with exclusive nonblocking lock twice (absolute paths)" { | 423 | test "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; |
| 118 | 425 | ||
| 119 | const allocator = std.testing.allocator; | 426 | const allocator = testing.allocator; |
| 120 | 427 | ||
| 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)" { |
| 126 | 433 | ||
| 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); |
| 130 | 437 | ||
| 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 | }; |
| 188 | 495 | ||
| 189 | fn run_lock_file_test(contexts: []FileLockTestContext) !void { | 496 | fn 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 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const os = std.os; | 2 | const os = std.os; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const math = std.math; | ||
| 4 | const Allocator = mem.Allocator; | 5 | const Allocator = mem.Allocator; |
| 5 | 6 | ||
| 6 | usingnamespace std.os.wasi; | 7 | usingnamespace std.os.wasi; |
| 7 | 8 | ||
| 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. |
| 11 | pub const PreopenType = enum { | 12 | pub const PreopenTypeTag = enum { |
| 12 | Dir, | 13 | Dir, |
| 13 | }; | 14 | }; |
| 14 | 15 | ||
| 16 | /// Type of WASI preopen. | ||
| 17 | /// | ||
| 18 | /// WASI currently offers only `Dir` as a valid preopen resource. | ||
| 19 | pub 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 descriptor | 42 | /// WASI preopen struct. This struct consists of a WASI file descriptor |
| 16 | /// and type of WASI preopen. It can be obtained directly from the WASI | 43 | /// 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, |
| 21 | 48 | ||
| 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 | }, | ||
| 27 | 51 | ||
| 28 | const Self = @This(); | 52 | /// Construct new `Preopen` instance. |
| 29 | 53 | pub fn new(fd: fd_t, preopen_type: PreopenType) Preopen { | |
| 30 | /// Construct new `Preopen` instance of type `PreopenType.Dir` from | 54 | 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 | }; |
| 47 | 60 | ||
| 48 | /// Dynamically-sized array list of WASI preopens. This struct is a | 61 | /// 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 { |
| 60 | 73 | ||
| 61 | const Self = @This(); | 74 | const Self = @This(); |
| 62 | 75 | ||
| 63 | pub const Error = os.UnexpectedError || Allocator.Error; | 76 | pub const Error = error{ OutOfMemory, Overflow } || os.UnexpectedError; |
| 64 | 77 | ||
| 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 before | 96 | /// 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 again | 105 | // 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 going | 119 | // 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 | } |
| 121 | 141 | ||
| 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 will | 145 | for (self.buffer.items) |*preopen| { |
| 126 | /// be needed in the future when WASI extends its capabilities to resources | 146 | 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(); |
| 157 | 171 | ||
| 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 { |
| 360 | 360 | ||
| 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); |
| 366 | 366 |
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 | }; |
| 22 | 22 | ||
| 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. |
| 24 | pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void { | 24 | pub fn hashPointer(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { |
| 25 | const info = @typeInfo(@TypeOf(key)); | 25 | const info = @typeInfo(@TypeOf(key)); |
| 26 | 26 | ||
| 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 | } |
| 54 | 54 | ||
| 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. |
| 56 | pub fn hashArray(hasher: var, key: var, comptime strat: HashStrategy) void { | 56 | pub 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 to | 59 | // 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 { |
| 73 | 73 | ||
| 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. |
| 76 | pub fn hash(hasher: var, key: var, comptime strat: HashStrategy) void { | 76 | pub 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. |
| 164 | pub fn autoHash(hasher: var, key: var) void { | 164 | pub 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 updated | 167 | 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 { |
| 181 | const testing = std.testing; | 181 | const testing = std.testing; |
| 182 | const Wyhash = std.hash.Wyhash; | 182 | const Wyhash = std.hash.Wyhash; |
| 183 | 183 | ||
| 184 | fn testHash(key: var) u64 { | 184 | fn 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 | } |
| 190 | 190 | ||
| 191 | fn testHashShallow(key: var) u64 { | 191 | fn 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 | } |
| 197 | 197 | ||
| 198 | fn testHashDeep(key: var) u64 { | 198 | fn 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 | } |
| 204 | 204 | ||
| 205 | fn testHashDeepRecursive(key: var) u64 { | 205 | fn 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 { |
| 88 | 88 | ||
| 89 | const block_size: usize = 8 * 8192; | 89 | const block_size: usize = 8 * 8192; |
| 90 | 90 | ||
| 91 | pub fn benchmarkHash(comptime H: var, bytes: usize) !Result { | 91 | pub 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 | } |
| 121 | 121 | ||
| 122 | pub fn benchmarkHashSmallKeys(comptime H: var, key_size: usize, bytes: usize) !Result { | 122 | pub 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 | } |
| 173 | 173 | ||
| 174 | pub fn main() !void { | 174 | pub fn main() !void { |
| 175 | const stdout = std.io.getStdOut().outStream(); | 175 | const stdout = std.io.getStdOut().writer(); |
| 176 | 176 | ||
| 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 | } |
| 253 | 253 | ||
| 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 | }; |
| 356 | 356 | ||
| 357 | fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 { | 357 | fn 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 | }; |
| 281 | 281 | ||
| 282 | fn SMHasherTest(comptime hash_fn: var, comptime hashbits: u32) u32 { | 282 | fn 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; |
| 9 | const Wyhash = std.hash.Wyhash; | 9 | const Wyhash = std.hash.Wyhash; |
| 10 | const Allocator = mem.Allocator; | 10 | const Allocator = mem.Allocator; |
| 11 | const builtin = @import("builtin"); | 11 | const builtin = @import("builtin"); |
| 12 | 12 | const hash_map = @This(); | |
| 13 | const want_modification_safety = std.debug.runtime_safety; | ||
| 14 | const debug_u32 = if (want_modification_safety) u32 else void; | ||
| 15 | 13 | ||
| 16 | pub fn AutoHashMap(comptime K: type, comptime V: type) type { | 14 | pub 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 | |||
| 18 | pub fn AutoHashMapUnmanaged(comptime K: type, comptime V: type) type { | ||
| 19 | return HashMapUnmanaged(K, V, getAutoHashFn(K), getAutoEqlFn(K), autoEqlIsCheap(K)); | ||
| 18 | } | 20 | } |
| 19 | 21 | ||
| 20 | /// Builtin hashmap for strings as keys. | 22 | /// Builtin hashmap for strings as keys. |
| 21 | pub fn StringHashMap(comptime V: type) type { | 23 | pub 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 | |||
| 27 | pub fn StringHashMapUnmanaged(comptime V: type) type { | ||
| 28 | return HashMapUnmanaged([]const u8, V, hashString, eqlString, true); | ||
| 23 | } | 29 | } |
| 24 | 30 | ||
| 25 | pub fn eqlString(a: []const u8, b: []const u8) bool { | 31 | pub 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 | } |
| 32 | 38 | ||
| 33 | pub 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. | ||
| 49 | pub 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, |
| 39 | 59 | ||
| 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; |
| 42 | 62 | 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 | }; | ||
| 65 | 64 | ||
| 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 returned | 68 | /// 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, | ||
| 74 | 70 | ||
| 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 modification | 73 | 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 | } |
| 90 | 77 | ||
| 91 | // Reset the iterator to the initial index | 78 | /// 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 | }; |
| 99 | 83 | ||
| 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 | } |
| 109 | 93 | ||
| 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 | } |
| 113 | 98 | ||
| 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 | } |
| 122 | 102 | ||
| 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 | } |
| 126 | 119 | ||
| 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 result | 121 | /// 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, and | 123 | /// Otherwise, puts a new item with undefined value, and |
| 131 | /// the kv pointer points to it. Caller should then initialize | 124 | /// 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 only | 127 | 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 | } |
| 151 | 129 | ||
| 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 | } | ||
| 156 | 182 | ||
| 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 | } |
| 159 | 187 | ||
| 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 if | 189 | /// If insertion happuns, asserts there is enough capacity without allocating. |
| 162 | // expected_count items are put into it | 190 | 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 | } |
| 168 | 193 | ||
| 169 | /// Increases capacity so that the hash map will be at most | 194 | pub fn getEntry(self: Self, key: K) ?*Entry { |
| 170 | /// 60% full when expected_count items are put into it | 195 | 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 | } |
| 176 | 197 | ||
| 177 | /// Sets the capacity to the new capacity if the new | 198 | 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 modulo | 202 | pub fn contains(self: Self, key: K) bool { |
| 182 | // optimization in the constrainIndex fn | 203 | 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. | ||
| 246 | pub 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; | ||
| 184 | 286 | ||
| 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 | } | ||
| 188 | 339 | ||
| 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 table | 344 | /// 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 | } |
| 202 | 415 | ||
| 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 | } |
| 208 | 424 | ||
| 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 | } |
| 213 | 431 | ||
| 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 | } | ||
| 217 | 439 | ||
| 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 | } |
| 222 | 447 | ||
| 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 | } |
| 226 | 467 | ||
| 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 | } |
| 233 | 499 | ||
| 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 | } |
| 237 | 503 | ||
| 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 | } |
| 241 | 507 | ||
| 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 | } |
| 275 | 528 | ||
| 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 | } |
| 280 | 534 | ||
| 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 | } |
| 289 | 538 | ||
| 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 | } |
| 299 | 550 | ||
| 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 capacity | 555 | 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; | ||
| 309 | 561 | ||
| 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 | } |
| 318 | 592 | ||
| 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 | } |
| 324 | 614 | ||
| 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); |
| 329 | 619 | 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]; |
| 349 | 628 | 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 rescue | 631 | .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 | } | ||
| 373 | 712 | ||
| 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 with | 716 | const start_index = header.constrainIndex(h); |
| 378 | // same key | 717 | 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 | } | ||
| 381 | 731 | ||
| 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 | } |
| 398 | 740 | ||
| 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; |
| 406 | 751 | 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 | |||
| 779 | const CapacityIndexType = enum { u8, u16, u32, usize }; | ||
| 780 | |||
| 781 | fn 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 | |||
| 791 | fn 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 | |||
| 800 | fn 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 | }; | ||
| 413 | 811 | ||
| 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 | } |
| 417 | 815 | ||
| 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 | } |
| 425 | 821 | ||
| 822 | /// This struct is trailed by an array of `Index(I)`, where `I` | ||
| 823 | /// and the array length are determined by `indexes_len`. | ||
| 824 | const 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 | |||
| 426 | test "basic hash map usage" { | 870 | test "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(); |
| 429 | 873 | ||
| 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); |
| 434 | 878 | ||
| 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); |
| 438 | 882 | ||
| 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); |
| 444 | 888 | ||
| 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); |
| 449 | 893 | ||
| 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); |
| 455 | 899 | ||
| 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); |
| 459 | 903 | ||
| 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); | ||
| 466 | 910 | ||
| 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(); |
| 499 | 943 | ||
| 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" { |
| 510 | 954 | ||
| 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(); |
| 532 | 976 | ||
| 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 putAssumeCapacity | 984 | // shouldn't resize from putAssumeCapacity |
| 541 | testing.expect(initialCapacity == map.entries.len); | 985 | testing.expect(initial_capacity == map.capacity()); |
| 986 | } | ||
| 987 | |||
| 988 | test "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 | } |
| 543 | 1006 | ||
| 544 | pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) { | 1007 | pub 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 | } |
| 577 | 1040 | ||
| 1041 | pub 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 | |||
| 578 | pub fn getAutoHashStratFn(comptime K: type, comptime strategy: std.hash.Strategy) (fn (K) u32) { | 1059 | pub 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; |
| 15 | 15 | ||
| 16 | const Allocator = mem.Allocator; | 16 | const Allocator = mem.Allocator; |
| 17 | 17 | ||
| 18 | usingnamespace if (comptime @hasDecl(c, "malloc_size")) | ||
| 19 | struct { | ||
| 20 | pub const supports_malloc_size = true; | ||
| 21 | pub const malloc_size = c.malloc_size; | ||
| 22 | } | ||
| 23 | else 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 | } | ||
| 28 | else | ||
| 29 | struct { | ||
| 30 | pub const supports_malloc_size = false; | ||
| 31 | }; | ||
| 32 | |||
| 18 | pub const c_allocator = &c_allocator_state; | 33 | pub const c_allocator = &c_allocator_state; |
| 19 | var c_allocator_state = Allocator{ | 34 | var c_allocator_state = Allocator{ |
| 20 | .reallocFn = cRealloc, | 35 | .allocFn = cAlloc, |
| 21 | .shrinkFn = cShrink, | 36 | .resizeFn = cResize, |
| 22 | }; | 37 | }; |
| 23 | 38 | ||
| 24 | fn cRealloc(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { | 39 | fn 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 | } |
| 30 | 55 | ||
| 31 | fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { | 56 | fn 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 | } |
| 36 | 72 | ||
| 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; |
| 45 | 81 | ||
| 46 | var page_allocator_state = Allocator{ | 82 | var page_allocator_state = Allocator{ |
| 47 | .reallocFn = PageAllocator.realloc, | 83 | .allocFn = PageAllocator.alloc, |
| 48 | .shrinkFn = PageAllocator.shrink, | 84 | .resizeFn = PageAllocator.resize, |
| 49 | }; | 85 | }; |
| 50 | var wasm_page_allocator_state = Allocator{ | 86 | var wasm_page_allocator_state = Allocator{ |
| 51 | .reallocFn = WasmPageAllocator.realloc, | 87 | .allocFn = WasmPageAllocator.alloc, |
| 52 | .shrinkFn = WasmPageAllocator.shrink, | 88 | .resizeFn = WasmPageAllocator.resize, |
| 53 | }; | 89 | }; |
| 54 | 90 | ||
| 55 | pub const direct_allocator = @compileError("deprecated; use std.heap.page_allocator"); | 91 | pub const direct_allocator = @compileError("deprecated; use std.heap.page_allocator"); |
| 56 | 92 | ||
| 93 | /// Verifies that the adjusted length will still map to the full length | ||
| 94 | pub 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 | |||
| 57 | const PageAllocator = struct { | 100 | const 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); | ||
| 60 | 104 | ||
| 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=42223 | 112 | // 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; |
| 75 | 119 | ||
| 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 | } |
| 80 | 124 | ||
| 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; |
| 84 | 128 | ||
| 85 | const final_addr = while (true) { | 129 | while (true) { |
| 86 | // Reserve a range of memory large enough to find a sufficiently | 130 | // 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; |
| 109 | 153 | ||
| 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 | } |
| 115 | 157 | ||
| 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)); |
| 126 | 169 | ||
| 127 | const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment); | 170 | const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment); |
| 128 | 171 | ||
| 129 | // Unmap the extra bytes that were only requested in order to guarantee | 172 | // 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 in | 173 | // 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 | } |
| 141 | 185 | ||
| 142 | return @intToPtr([*]u8, aligned_addr)[0..n]; | 186 | return @intToPtr([*]u8, aligned_addr)[0..alignPageAllocLen(alignedLen, n, len_align)]; |
| 143 | } | 187 | } |
| 144 | 188 | ||
| 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 the | 199 | // 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 only | 209 | // 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 | } | ||
| 220 | 225 | ||
| 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 | }; | ||
| 237 | 229 | ||
| 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 | }; |
| 252 | 242 | ||
| ... | @@ -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/3806 | 289 | // 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); |
| 301 | 291 | ||
| 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 | } |
| 339 | 330 | ||
| 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 | } |
| 343 | 334 | ||
| 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 | } |
| 349 | 347 | ||
| 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 | } |
| 354 | 352 | ||
| 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 | } |
| 359 | 365 | return @intCast(usize, aligned_page_idx); | |
| 360 | return @intCast(usize, prev_page_count); | ||
| 361 | } | 366 | } |
| 362 | 367 | ||
| 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 | } |
| 367 | 372 | 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 | } |
| 380 | 387 | ||
| 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); |
| 385 | 392 | 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 | } |
| 406 | 397 | 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 | }; |
| 410 | 400 | ||
| ... | @@ -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 | } |
| 433 | 423 | ||
| 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{}; | ||
| 437 | 430 | ||
| 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)); |
| 454 | 447 | 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 | } |
| 466 | 454 | ||
| 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 | } |
| 480 | 461 | ||
| 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 that | 479 | 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 | }; |
| 508 | 485 | ||
| 486 | fn sliceContainsPtr(container: []u8, ptr: [*]u8) bool { | ||
| 487 | return @ptrToInt(ptr) >= @ptrToInt(container.ptr) and | ||
| 488 | @ptrToInt(ptr) < (@ptrToInt(container.ptr) + container.len); | ||
| 489 | } | ||
| 490 | |||
| 491 | fn 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 | |||
| 509 | pub const FixedBufferAllocator = struct { | 496 | pub 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 | } |
| 524 | 511 | ||
| 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 | } |
| 539 | 540 | ||
| 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 and | 544 | |
| 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 | } | ||
| 561 | 556 | ||
| 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 | } |
| 565 | 564 | ||
| 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 | } |
| 591 | 590 | ||
| 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 | } |
| 606 | 605 | ||
| 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 | } |
| 654 | 638 | ||
| 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) and | 641 | 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 | } |
| 686 | 644 | ||
| 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) and | 647 | 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" { |
| 718 | 663 | ||
| 719 | test "WasmPageAllocator internals" { | 664 | test "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. |
| 724 | 669 | ||
| 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 | } |
| 776 | 726 | ||
| 777 | test "HeapAllocator" { | 727 | test "HeapAllocator" { |
| ... | @@ -799,7 +749,7 @@ test "ArenaAllocator" { | ... | @@ -799,7 +749,7 @@ test "ArenaAllocator" { |
| 799 | 749 | ||
| 800 | var test_fixed_buffer_allocator_memory: [800000 * @sizeOf(u64)]u8 = undefined; | 750 | var test_fixed_buffer_allocator_memory: [800000 * @sizeOf(u64)]u8 = undefined; |
| 801 | test "FixedBufferAllocator" { | 751 | test "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..])); |
| 803 | 753 | ||
| 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 | } |
| 867 | 817 | ||
| 868 | fn testAllocator(allocator: *mem.Allocator) !void { | 818 | pub 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 | } |
| 895 | 848 | ||
| 896 | fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !void { | 849 | pub fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void { |
| 850 | var validationAllocator = mem.validationWrap(base_allocator); | ||
| 851 | const allocator = &validationAllocator.allocator; | ||
| 852 | |||
| 897 | // initial | 853 | // 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 | } |
| 919 | 875 | ||
| 920 | fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void { | 876 | pub 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 or | 880 | //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 | } |
| 948 | 907 | ||
| 949 | fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!void { | 908 | pub 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; |
| 952 | 914 |
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 | } |
| 63 | 62 | ||
| 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); |
| 66 | 65 | ||
| 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 | } |
| 25 | 25 | ||
| 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 | } |
| 41 | 37 | ||
| 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 | } |
| 54 | 60 | ||
| 55 | pub fn loggingAllocator( | 61 | pub 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 | } |
| 61 | 67 | ||
| 62 | test "LoggingAllocator" { | 68 | test "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); |
| 65 | 71 | ||
| 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; | ||
| 67 | 75 | ||
| 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); | ||
| 70 | 81 | ||
| 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 | } |
| 28 | 28 | ||
| 29 | const HeaderEntry = struct { | 29 | const 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 { |
| 36 | 35 | ||
| 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 reference | 38 | .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 | } |
| 45 | 43 | ||
| 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 | } |
| 49 | 47 | ||
| 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 { |
| 85 | 83 | ||
| 86 | test "HeaderEntry" { | 84 | test "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); |
| 92 | 90 | ||
| 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); |
| 95 | 93 | ||
| 96 | // shorter value | 94 | // 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 | } |
| 100 | 98 | ||
| 101 | const HeaderList = std.ArrayList(HeaderEntry); | 99 | const HeaderList = std.ArrayListUnmanaged(HeaderEntry); |
| 102 | const HeaderIndexList = std.ArrayList(usize); | 100 | const HeaderIndexList = std.ArrayListUnmanaged(usize); |
| 103 | const HeaderIndex = std.StringHashMap(HeaderIndexList); | 101 | const HeaderIndex = std.StringHashMapUnmanaged(HeaderIndexList); |
| 104 | 102 | ||
| 105 | pub const Headers = struct { | 103 | pub const Headers = struct { |
| 106 | // the owned header field name is stored in the index as part of the key | 104 | // 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 | } |
| 120 | 118 | ||
| 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 | } |
| 138 | 136 | ||
| 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 | } |
| 149 | 147 | ||
| 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 | } |
| 153 | 151 | ||
| 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 { |
| 194 | 192 | ||
| 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 backwards | 197 | // 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 { |
| 216 | 214 | ||
| 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 index | 225 | // 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 index | 235 | // 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 | } |
| 238 | 240 | ||
| 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 index | 250 | // 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 index | 260 | // 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 | } |
| 260 | 265 | ||
| ... | @@ -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 | } |
| 275 | 276 | ||
| 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 { |
| 279 | 280 | ||
| 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.join | 303 | // 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 | } |
| 327 | 328 | ||
| 328 | fn rebuild_index(self: *Self) void { | 329 | fn rebuildIndex(self: *Self) void { |
| 329 | { // clear out the indexes | 330 | // 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 before | 334 | // 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 | } |
| 343 | 339 | ||
| 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 | } |
| 348 | 344 | ||
| 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); |
| 496 | 492 | ||
| 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 | } |
| 501 | 497 | ||
| 502 | test "Headers.get" { | 498 | test "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 { |
| 170 | 170 | ||
| 171 | pub fn bitReader( | 171 | pub 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 of | 34 | /// 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 there | 35 | /// 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; |
| 39 | 39 | ||
| 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 { |
| 145 | 145 | ||
| 146 | pub fn bitWriter( | 146 | pub 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 @@ |
| 2 | pub const BufferedOutStream = @import("./buffered_writer.zig").BufferedWriter; | 2 | pub const BufferedOutStream = @import("./buffered_writer.zig").BufferedWriter; |
| 3 | 3 | ||
| 4 | /// Deprecated: use `std.io.buffered_writer.bufferedWriter` | 4 | /// Deprecated: use `std.io.buffered_writer.bufferedWriter` |
| 5 | pub const bufferedOutStream = @import("./buffered_writer.zig").bufferedWriter | 5 | pub 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 | } |
| 50 | 50 | ||
| 51 | pub fn bufferedReader(underlying_stream: var) BufferedReader(4096, @TypeOf(underlying_stream)) { | 51 | pub fn bufferedReader(underlying_stream: anytype) BufferedReader(4096, @TypeOf(underlying_stream)) { |
| 52 | return .{ .unbuffered_reader = underlying_stream }; | 52 | return .{ .unbuffered_reader = underlying_stream }; |
| 53 | } | 53 | } |
| 54 | 54 |
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 | } |
| 45 | 45 | ||
| 46 | pub fn bufferedWriter(underlying_stream: var) BufferedWriter(4096, @TypeOf(underlying_stream)) { | 46 | pub 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 | } |
| 34 | 34 | ||
| 35 | pub fn countingWriter(child_stream: var) CountingWriter(@TypeOf(child_stream)) { | 35 | pub 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 | } |
| 38 | 38 |
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 | } |
| 129 | 129 | ||
| 130 | pub fn fixedBufferStream(buffer: var) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) { | 130 | pub 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 | } |
| 133 | 133 |
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 | } |
| 45 | 45 | ||
| 46 | pub fn multiWriter(streams: var) MultiWriter(@TypeOf(streams)) { | 46 | pub fn multiWriter(streams: anytype) MultiWriter(@TypeOf(streams)) { |
| 47 | return .{ .streams = streams }; | 47 | return .{ .streams = streams }; |
| 48 | } | 48 | } |
| 49 | 49 |
lib/std/io/peek_stream.zig+1-1| ... | @@ -80,7 +80,7 @@ pub fn PeekStream( | ... | @@ -80,7 +80,7 @@ pub fn PeekStream( |
| 80 | 80 | ||
| 81 | pub fn peekStream( | 81 | pub 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 | } |
| 42 | 42 | ||
| 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 | }; |
| 17 | 17 | ||
| 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 a | 21 | /// 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) !void | 23 | /// ``` |
| 24 | /// which will be called when the deserializer is used to deserialize | 24 | /// pub fn deserialize(self: *Self, deserializer: anytype) !void |
| 25 | /// that type. It will pass a pointer to the type instance to deserialize | 25 | /// ``` |
| 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. | ||
| 27 | pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime ReaderType: type) type { | 29 | pub 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 | } |
| 94 | 96 | ||
| 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)); |
| 99 | 101 | ||
| ... | @@ -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); |
| 110 | 112 | ||
| 111 | //custom deserializer: fn(self: *Self, deserializer: var) !void | 113 | //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); |
| 113 | 115 | ||
| 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, |
| 190 | pub fn deserializer( | 192 | pub 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 | } |
| 197 | 199 | ||
| 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 done | 202 | /// 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 case | 204 | /// 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 specified | 205 | /// 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 a | 207 | /// 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) !void | 209 | /// ``` |
| 208 | /// which will be called when the serializer is used to serialize that type. It will | 210 | /// pub fn serialize(self: Self, serializer: anytype) !void |
| 209 | /// pass a const pointer to the type instance to be serialized and a pointer | 211 | /// ``` |
| 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. | ||
| 211 | pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime OutStreamType: type) type { | 215 | pub 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 | } |
| 231 | 235 | ||
| 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)); |
| 235 | 239 | ||
| ... | @@ -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 | } |
| 262 | 266 | ||
| 263 | /// Serializes the passed value into the stream | 267 | /// 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); |
| 266 | 270 | ||
| 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 | } |
| 272 | 276 | ||
| 273 | //custom serializer: fn(self: Self, serializer: var) !void | 277 | //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); |
| 275 | 279 | ||
| 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 |
| 346 | pub fn serializer( | 350 | pub 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 | } |
| 464 | 468 | ||
| 465 | fn testAlternateSerializer(self: var, _serializer: var) !void { | 469 | fn testAlternateSerializer(self: anytype, _serializer: anytype) !void { |
| 466 | try _serializer.serialize(self.f_f16); | 470 | try _serializer.serialize(self.f_f16); |
| 467 | } | 471 | } |
| 468 | 472 | ||
| ... | @@ -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, |
| 505 | 509 | ||
| 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 | } |
| 26 | 26 | ||
| 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 | } |
| 30 | 30 |
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, |
| 240 | 240 | ||
| 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(); |
| 1290 | 1290 | ||
| 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" { |
| 2149 | 2149 | ||
| 2150 | var root = tree.root; | 2150 | var root = tree.root; |
| 2151 | 2151 | ||
| 2152 | var image = root.Object.get("Image").?.value; | 2152 | var image = root.Object.get("Image").?; |
| 2153 | 2153 | ||
| 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); |
| 2156 | 2156 | ||
| 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); |
| 2159 | 2159 | ||
| 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")); |
| 2162 | 2162 | ||
| 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); |
| 2165 | 2165 | ||
| 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); |
| 2168 | 2168 | ||
| 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")); |
| 2171 | 2171 | ||
| 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 | } |
| 2175 | 2175 | ||
| ... | @@ -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(); |
| 2219 | 2219 | ||
| 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 | } |
| 2227 | 2227 | ||
| 2228 | fn test_parse(arena_allocator: *std.mem.Allocator, json_str: []const u8) !Value { | 2228 | fn 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 | } |
| 2250 | 2250 | ||
| 2251 | test "escaped characters" { | 2251 | test "escaped characters" { |
| ... | @@ -2271,16 +2271,16 @@ test "escaped characters" { | ... | @@ -2271,16 +2271,16 @@ test "escaped characters" { |
| 2271 | 2271 | ||
| 2272 | const obj = (try test_parse(&arena_allocator.allocator, input)).Object; | 2272 | const obj = (try test_parse(&arena_allocator.allocator, input)).Object; |
| 2273 | 2273 | ||
| 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 | } |
| 2285 | 2285 | ||
| 2286 | test "string copy option" { | 2286 | test "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; |
| 2307 | 2307 | ||
| 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 | } |
| 2311 | 2311 | ||
| 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]; |
| 2314 | 2314 | ||
| 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 { |
| 2338 | 2338 | ||
| 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 { |
| 2380 | 2380 | ||
| 2381 | fn outputUnicodeEscape( | 2381 | fn 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 | } |
| 2403 | 2403 | ||
| 2404 | pub fn stringify( | 2404 | pub 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 | } |
| 2586 | 2586 | ||
| 2587 | fn teststringify(expected: []const u8, value: var, options: StringifyOptions) !void { | 2587 | fn 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" { |
| 2770 | test "stringify vector" { | 2770 | test "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 losslessly | 153 | /// 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 | } |
| 217 | 217 | ||
| 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 | } |
| 225 | 225 | ||
| 226 | pub fn writeStream( | 226 | pub 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 @@ | ||
| 1 | const std = @import("std.zig"); | ||
| 2 | const builtin = std.builtin; | ||
| 3 | const 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 | |||
| 59 | pub 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. | ||
| 86 | pub 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. | ||
| 95 | pub const level: Level = if (@hasDecl(root, "log_level")) | ||
| 96 | root.log_level | ||
| 97 | else | ||
| 98 | default_level; | ||
| 99 | |||
| 100 | fn 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. | ||
| 120 | pub 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). | ||
| 131 | pub 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. | ||
| 143 | pub 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. | ||
| 154 | pub 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. | ||
| 166 | pub 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. | ||
| 176 | pub 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. | ||
| 186 | pub 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. | ||
| 196 | pub 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 | } |
| 105 | 105 | ||
| 106 | // TODO: Hide the following in an internal module. | 106 | // TODO: Hide the following in an internal module. |
| 107 | pub fn forceEval(value: var) void { | 107 | pub 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 { |
| 254 | 259 | ||
| 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. |
| 257 | pub fn min(x: var, y: var) Min(@TypeOf(x), @TypeOf(y)) { | 262 | pub 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 this | 265 | // 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 | } |
| 307 | 312 | ||
| 308 | pub fn max(x: var, y: var) @TypeOf(x, y) { | 313 | pub 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 | } |
| 311 | 316 | ||
| ... | @@ -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 | } |
| 315 | 320 | ||
| 316 | pub fn clamp(val: var, lower: var, upper: var) @TypeOf(val, lower, upper) { | 321 | pub 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 | } |
| 351 | 356 | ||
| 352 | pub fn negate(x: var) !@TypeOf(x) { | 357 | pub fn negate(x: anytype) !@TypeOf(x) { |
| 353 | return sub(@TypeOf(x), 0, x); | 358 | return sub(@TypeOf(x), 0, x); |
| 354 | } | 359 | } |
| 355 | 360 | ||
| ... | @@ -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 { |
| 360 | 365 | ||
| 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. |
| 363 | pub fn shl(comptime T: type, a: T, shift_amt: var) T { | 368 | pub 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); |
| 366 | 371 | ||
| ... | @@ -386,7 +391,7 @@ test "math.shl" { | ... | @@ -386,7 +391,7 @@ test "math.shl" { |
| 386 | 391 | ||
| 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. |
| 389 | pub fn shr(comptime T: type, a: T, shift_amt: var) T { | 394 | pub 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); |
| 392 | 397 | ||
| ... | @@ -414,7 +419,7 @@ test "math.shr" { | ... | @@ -414,7 +419,7 @@ test "math.shr" { |
| 414 | 419 | ||
| 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. |
| 417 | pub fn rotr(comptime T: type, x: T, r: var) T { | 422 | pub 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" { |
| 433 | 438 | ||
| 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. |
| 436 | pub fn rotl(comptime T: type, x: T, r: var) T { | 441 | pub 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 | } |
| 538 | 543 | ||
| 539 | pub fn absInt(x: var) !@TypeOf(x) { | 544 | pub 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 absInt | 546 | comptime assert(@typeInfo(T) == .Int); // must pass an integer to absInt |
| 542 | comptime assert(T.is_signed); // must pass a signed integer to absInt | 547 | 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 { |
| 684 | 689 | ||
| 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. |
| 687 | pub fn absCast(x: var) switch (@typeInfo(@TypeOf(x))) { | 692 | pub 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" { |
| 719 | 724 | ||
| 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. |
| 722 | pub fn negateCast(x: var) !std.meta.Int(true, @TypeOf(x).bit_count) { | 727 | pub 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); |
| 724 | 729 | ||
| 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" { |
| 742 | 747 | ||
| 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. |
| 745 | pub fn cast(comptime T: type, x: var) (error{Overflow}!T) { | 750 | pub fn cast(comptime T: type, x: anytype) (error{Overflow}!T) { |
| 746 | comptime assert(@typeInfo(T) == .Int); // must pass an integer | 751 | comptime assert(@typeInfo(T) == .Int); // must pass an integer |
| 747 | comptime assert(@typeInfo(@TypeOf(x)) == .Int); // must pass an integer | 752 | 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" { |
| 767 | pub const AlignCastError = error{UnalignedMemory}; | 772 | pub const AlignCastError = error{UnalignedMemory}; |
| 768 | 773 | ||
| 769 | /// Align cast a pointer but return an error if it's the wrong alignment | 774 | /// Align cast a pointer but return an error if it's the wrong alignment |
| 770 | pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@TypeOf(@alignCast(alignment, ptr)) { | 775 | pub 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 | } |
| 777 | 782 | ||
| 778 | pub fn isPowerOfTwo(v: var) bool { | 783 | pub 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 | } |
| 894 | 899 | ||
| 895 | pub fn lossyCast(comptime T: type, value: var) T { | 900 | pub 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 | }; |
| 1027 | 1032 | ||
| 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. |
| 1029 | pub fn order(a: var, b: var) Order { | 1034 | pub 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 { |
| 1042 | pub const CompareOperator = enum { | 1047 | pub 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 the | 1062 | /// This function does the same thing as comparison operators, however the |
| 1063 | /// operator is a runtime-known enum value. Works on any operands that | 1063 | /// operator is a runtime-known enum value. Works on any operands that |
| 1064 | /// support comparison operators. | 1064 | /// support comparison operators. |
| 1065 | pub fn compare(a: var, op: CompareOperator, b: var) bool { | 1065 | pub 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 > 1 | 14 | /// - acos(x) = nan if x < -1 or x > 1 |
| 15 | pub fn acos(x: var) @TypeOf(x) { | 15 | pub 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 < 1 | 15 | /// - acosh(x) = snan if x < 1 |
| 16 | /// - acosh(nan) = nan | 16 | /// - acosh(nan) = nan |
| 17 | pub fn acosh(x: var) @TypeOf(x) { | 17 | pub 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) = +-0 | 14 | /// - asin(+-0) = +-0 |
| 15 | /// - asin(x) = nan if x < -1 or x > 1 | 15 | /// - asin(x) = nan if x < -1 or x > 1 |
| 16 | pub fn asin(x: var) @TypeOf(x) { | 16 | pub 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) = +-0 | 15 | /// - asinh(+-0) = +-0 |
| 16 | /// - asinh(+-inf) = +-inf | 16 | /// - asinh(+-inf) = +-inf |
| 17 | /// - asinh(nan) = nan | 17 | /// - asinh(nan) = nan |
| 18 | pub fn asinh(x: var) @TypeOf(x) { | 18 | pub 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) = +-0 | 14 | /// - atan(+-0) = +-0 |
| 15 | /// - atan(+-inf) = +-pi/2 | 15 | /// - atan(+-inf) = +-pi/2 |
| 16 | pub fn atan(x: var) @TypeOf(x) { | 16 | pub 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 signal | 15 | /// - atanh(+-1) = +-inf with signal |
| 16 | /// - atanh(x) = nan if |x| > 1 with signal | 16 | /// - atanh(x) = nan if |x| > 1 with signal |
| 17 | /// - atanh(nan) = nan | 17 | /// - atanh(nan) = nan |
| 18 | pub fn atanh(x: var) @TypeOf(x) { | 18 | pub 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; |
| 12 | 12 | ||
| 13 | /// Returns the number of limbs needed to store `scalar`, which must be a | 13 | /// Returns the number of limbs needed to store `scalar`, which must be a |
| 14 | /// primitive integer value. | 14 | /// primitive integer value. |
| 15 | pub fn calcLimbLen(scalar: var) usize { | 15 | pub 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 buffer | 170 | /// 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); |
| 174 | 174 | ||
| 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 by | 282 | /// 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); |
| 1106 | 1106 | ||
| 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 | } |
| 1262 | 1262 | ||
| 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 | } |
| 1497 | 1497 | ||
| 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 | } |
| 44 | 44 | ||
| 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 | } |
| 281 | 281 | ||
| 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); |
| 286 | 286 |
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) = +-0 | 14 | /// - cbrt(+-0) = +-0 |
| 15 | /// - cbrt(+-inf) = +-inf | 15 | /// - cbrt(+-inf) = +-inf |
| 16 | /// - cbrt(nan) = nan | 16 | /// - cbrt(nan) = nan |
| 17 | pub fn cbrt(x: var) @TypeOf(x) { | 17 | pub 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) = +-0 | 15 | /// - ceil(+-0) = +-0 |
| 16 | /// - ceil(+-inf) = +-inf | 16 | /// - ceil(+-inf) = +-inf |
| 17 | /// - ceil(nan) = nan | 17 | /// - ceil(nan) = nan |
| 18 | pub fn ceil(x: var) @TypeOf(x) { | 18 | pub 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 | } |
| 88 | 89 | ||
| 90 | fn 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 | |||
| 89 | test "math.ceil" { | 117 | test "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 | } |
| 93 | 122 | ||
| 94 | test "math.ceil32" { | 123 | test "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 | } |
| 105 | 134 | ||
| 135 | test "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 | |||
| 106 | test "math.ceil32.special" { | 141 | test "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 | |||
| 157 | test "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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the absolute value (modulus) of z. | 7 | /// Returns the absolute value (modulus) of z. |
| 8 | pub fn abs(z: var) @TypeOf(z.re) { | 8 | pub 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the arc-cosine of z. | 7 | /// Returns the arc-cosine of z. |
| 8 | pub fn acos(z: var) Complex(@TypeOf(z.re)) { | 8 | pub 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the hyperbolic arc-cosine of z. | 7 | /// Returns the hyperbolic arc-cosine of z. |
| 8 | pub fn acosh(z: var) Complex(@TypeOf(z.re)) { | 8 | pub 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the angular component (in radians) of z. | 7 | /// Returns the angular component (in radians) of z. |
| 8 | pub fn arg(z: var) @TypeOf(z.re) { | 8 | pub 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | // Returns the arc-sine of z. | 7 | // Returns the arc-sine of z. |
| 8 | pub fn asin(z: var) Complex(@TypeOf(z.re)) { | 8 | pub 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the hyperbolic arc-sine of z. | 7 | /// Returns the hyperbolic arc-sine of z. |
| 8 | pub fn asinh(z: var) Complex(@TypeOf(z.re)) { | 8 | pub 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; |
| 12 | const Complex = cmath.Complex; | 12 | const Complex = cmath.Complex; |
| 13 | 13 | ||
| 14 | /// Returns the arc-tangent of z. | 14 | /// Returns the arc-tangent of z. |
| 15 | pub fn atan(z: var) @TypeOf(z) { | 15 | pub 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the hyperbolic arc-tangent of z. | 7 | /// Returns the hyperbolic arc-tangent of z. |
| 8 | pub fn atanh(z: var) Complex(@TypeOf(z.re)) { | 8 | pub 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the complex conjugate of z. | 7 | /// Returns the complex conjugate of z. |
| 8 | pub fn conj(z: var) Complex(@TypeOf(z.re)) { | 8 | pub 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the cosine of z. | 7 | /// Returns the cosine of z. |
| 8 | pub fn cos(z: var) Complex(@TypeOf(z.re)) { | 8 | pub 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; |
| 14 | const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; | 14 | const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; |
| 15 | 15 | ||
| 16 | /// Returns the hyperbolic arc-cosine of z. | 16 | /// Returns the hyperbolic arc-cosine of z. |
| 17 | pub fn cosh(z: var) Complex(@TypeOf(z.re)) { | 17 | pub 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; |
| 14 | const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; | 14 | const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; |
| 15 | 15 | ||
| 16 | /// Returns e raised to the power of z (e^z). | 16 | /// Returns e raised to the power of z (e^z). |
| 17 | pub fn exp(z: var) @TypeOf(z) { | 17 | pub fn exp(z: anytype) @TypeOf(z) { |
| 18 | const T = @TypeOf(z.re); | 18 | const T = @TypeOf(z.re); |
| 19 | 19 | ||
| 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; |
| 11 | const Complex = cmath.Complex; | 11 | const Complex = cmath.Complex; |
| 12 | 12 | ||
| 13 | /// Returns exp(z) scaled to avoid overflow. | 13 | /// Returns exp(z) scaled to avoid overflow. |
| 14 | pub fn ldexp_cexp(z: var, expt: i32) @TypeOf(z) { | 14 | pub fn ldexp_cexp(z: anytype, expt: i32) @TypeOf(z) { |
| 15 | const T = @TypeOf(z.re); | 15 | const T = @TypeOf(z.re); |
| 16 | 16 | ||
| 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the natural logarithm of z. | 7 | /// Returns the natural logarithm of z. |
| 8 | pub fn log(z: var) Complex(@TypeOf(z.re)) { | 8 | pub 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the projection of z onto the riemann sphere. | 7 | /// Returns the projection of z onto the riemann sphere. |
| 8 | pub fn proj(z: var) Complex(@TypeOf(z.re)) { | 8 | pub fn proj(z: anytype) Complex(@TypeOf(z.re)) { |
| 9 | const T = @TypeOf(z.re); | 9 | const T = @TypeOf(z.re); |
| 10 | 10 | ||
| 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the sine of z. | 7 | /// Returns the sine of z. |
| 8 | pub fn sin(z: var) Complex(@TypeOf(z.re)) { | 8 | pub 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; |
| 14 | const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; | 14 | const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; |
| 15 | 15 | ||
| 16 | /// Returns the hyperbolic sine of z. | 16 | /// Returns the hyperbolic sine of z. |
| 17 | pub fn sinh(z: var) @TypeOf(z) { | 17 | pub 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; |
| 12 | 12 | ||
| 13 | /// Returns the square root of z. The real and imaginary parts of the result have the same sign | 13 | /// 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. |
| 15 | pub fn sqrt(z: var) @TypeOf(z) { | 15 | pub fn sqrt(z: anytype) @TypeOf(z) { |
| 16 | const T = @TypeOf(z.re); | 16 | const T = @TypeOf(z.re); |
| 17 | 17 | ||
| 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; |
| 5 | const Complex = cmath.Complex; | 5 | const Complex = cmath.Complex; |
| 6 | 6 | ||
| 7 | /// Returns the tanget of z. | 7 | /// Returns the tanget of z. |
| 8 | pub fn tan(z: var) Complex(@TypeOf(z.re)) { | 8 | pub 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; |
| 12 | const Complex = cmath.Complex; | 12 | const Complex = cmath.Complex; |
| 13 | 13 | ||
| 14 | /// Returns the hyperbolic tangent of z. | 14 | /// Returns the hyperbolic tangent of z. |
| 15 | pub fn tanh(z: var) @TypeOf(z) { | 15 | pub 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) = nan | 14 | /// - cos(+-inf) = nan |
| 15 | /// - cos(nan) = nan | 15 | /// - cos(nan) = nan |
| 16 | pub fn cos(x: var) @TypeOf(x) { | 16 | pub 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) = 1 | 17 | /// - cosh(+-0) = 1 |
| 18 | /// - cosh(+-inf) = +inf | 18 | /// - cosh(+-inf) = +inf |
| 19 | /// - cosh(nan) = nan | 19 | /// - cosh(nan) = nan |
| 20 | pub fn cosh(x: var) @TypeOf(x) { | 20 | pub 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) = +inf | 15 | /// - exp(+inf) = +inf |
| 16 | /// - exp(nan) = nan | 16 | /// - exp(nan) = nan |
| 17 | pub fn exp(x: var) @TypeOf(x) { | 17 | pub 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) = +inf | 14 | /// - exp2(+inf) = +inf |
| 15 | /// - exp2(nan) = nan | 15 | /// - exp2(nan) = nan |
| 16 | pub fn exp2(x: var) @TypeOf(x) { | 16 | pub 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) = +inf | 18 | /// - expm1(+inf) = +inf |
| 19 | /// - expm1(-inf) = -1 | 19 | /// - expm1(-inf) = -1 |
| 20 | /// - expm1(nan) = nan | 20 | /// - expm1(nan) = nan |
| 21 | pub fn expm1(x: var) @TypeOf(x) { | 21 | pub 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 @@ |
| 7 | const math = @import("../math.zig"); | 7 | const math = @import("../math.zig"); |
| 8 | 8 | ||
| 9 | /// Returns exp(x) / 2 for x >= log(maxFloat(T)). | 9 | /// Returns exp(x) / 2 for x >= log(maxFloat(T)). |
| 10 | pub fn expo2(x: var) @TypeOf(x) { | 10 | pub 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) = +inf | 15 | /// - fabs(+-inf) = +inf |
| 16 | /// - fabs(nan) = nan | 16 | /// - fabs(nan) = nan |
| 17 | pub fn fabs(x: var) @TypeOf(x) { | 17 | pub 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) = +-0 | 15 | /// - floor(+-0) = +-0 |
| 16 | /// - floor(+-inf) = +-inf | 16 | /// - floor(+-inf) = +-inf |
| 17 | /// - floor(nan) = nan | 17 | /// - floor(nan) = nan |
| 18 | pub fn floor(x: var) @TypeOf(x) { | 18 | pub 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 | } |
| 124 | 125 | ||
| 126 | fn 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 | |||
| 125 | test "math.floor" { | 153 | test "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 | } |
| 130 | 159 | ||
| 131 | test "math.floor16" { | 160 | test "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 | } |
| 148 | 177 | ||
| 178 | test "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 | |||
| 149 | test "math.floor16.special" { | 184 | test "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 | |||
| 208 | test "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, 0 | 24 | /// - frexp(+-0) = +-0, 0 |
| 25 | /// - frexp(+-inf) = +-inf, 0 | 25 | /// - frexp(+-inf) = +-inf, 0 |
| 26 | /// - frexp(nan) = nan, undefined | 26 | /// - frexp(nan) = nan, undefined |
| 27 | pub fn frexp(x: var) frexp_result(@TypeOf(x)) { | 27 | pub 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) |
| 19 | pub fn ilogb(x: var) i32 { | 19 | pub 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; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
| 5 | 5 | ||
| 6 | /// Returns whether x is a finite value. | 6 | /// Returns whether x is a finite value. |
| 7 | pub fn isFinite(x: var) bool { | 7 | pub 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; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
| 5 | 5 | ||
| 6 | /// Returns whether x is an infinity, ignoring sign. | 6 | /// Returns whether x is an infinity, ignoring sign. |
| 7 | pub fn isInf(x: var) bool { | 7 | pub 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 | } |
| 31 | 31 | ||
| 32 | /// Returns whether x is an infinity with a positive sign. | 32 | /// Returns whether x is an infinity with a positive sign. |
| 33 | pub fn isPositiveInf(x: var) bool { | 33 | pub 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 | } |
| 53 | 53 | ||
| 54 | /// Returns whether x is an infinity with a negative sign. | 54 | /// Returns whether x is an infinity with a negative sign. |
| 55 | pub fn isNegativeInf(x: var) bool { | 55 | pub 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; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
| 5 | 5 | ||
| 6 | /// Returns whether x is a nan. | 6 | /// Returns whether x is a nan. |
| 7 | pub fn isNan(x: var) bool { | 7 | pub fn isNan(x: anytype) bool { |
| 8 | return x != x; | 8 | return x != x; |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | /// Returns whether x is a signalling nan. | 11 | /// Returns whether x is a signalling nan. |
| 12 | pub fn isSignalNan(x: var) bool { | 12 | pub fn isSignalNan(x: anytype) bool { |
| 13 | // Note: A signalling nan is identical to a standard nan right now but may have a different bit | 13 | // 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; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
| 5 | 5 | ||
| 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). |
| 7 | pub fn isNormal(x: var) bool { | 7 | pub 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) = -inf | 15 | /// - ln(0) = -inf |
| 16 | /// - ln(x) = nan if x < 0 | 16 | /// - ln(x) = nan if x < 0 |
| 17 | /// - ln(nan) = nan | 17 | /// - ln(nan) = nan |
| 18 | pub fn ln(x: var) @TypeOf(x) { | 18 | pub 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) = -inf | 16 | /// - log10(0) = -inf |
| 17 | /// - log10(x) = nan if x < 0 | 17 | /// - log10(x) = nan if x < 0 |
| 18 | /// - log10(nan) = nan | 18 | /// - log10(nan) = nan |
| 19 | pub fn log10(x: var) @TypeOf(x) { | 19 | pub 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) = -inf | 17 | /// - log1p(-1) = -inf |
| 18 | /// - log1p(x) = nan if x < -1 | 18 | /// - log1p(x) = nan if x < -1 |
| 19 | /// - log1p(nan) = nan | 19 | /// - log1p(nan) = nan |
| 20 | pub fn log1p(x: var) @TypeOf(x) { | 20 | pub 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) = -inf | 16 | /// - log2(0) = -inf |
| 17 | /// - log2(x) = nan if x < 0 | 17 | /// - log2(x) = nan if x < 0 |
| 18 | /// - log2(nan) = nan | 18 | /// - log2(nan) = nan |
| 19 | pub fn log2(x: var) @TypeOf(x) { | 19 | pub 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, nan | 25 | /// - modf(+-inf) = +-inf, nan |
| 26 | /// - modf(nan) = nan, nan | 26 | /// - modf(nan) = nan, nan |
| 27 | pub fn modf(x: var) modf_result(@TypeOf(x)) { | 27 | pub 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) = +-0 | 15 | /// - round(+-0) = +-0 |
| 16 | /// - round(+-inf) = +-inf | 16 | /// - round(+-inf) = +-inf |
| 17 | /// - round(nan) = nan | 17 | /// - round(nan) = nan |
| 18 | pub fn round(x: var) @TypeOf(x) { | 18 | pub 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 | } |
| 92 | 93 | ||
| 94 | fn 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 | |||
| 93 | test "math.round" { | 127 | test "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 | } |
| 97 | 132 | ||
| 98 | test "math.round32" { | 133 | test "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 | } |
| 111 | 146 | ||
| 147 | test "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 | |||
| 112 | test "math.round32.special" { | 154 | test "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 | |||
| 170 | test "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; |
| 9 | const expect = std.testing.expect; | 9 | const expect = std.testing.expect; |
| 10 | 10 | ||
| 11 | /// Returns x * 2^n. | 11 | /// Returns x * 2^n. |
| 12 | pub fn scalbn(x: var, n: i32) @TypeOf(x) { | 12 | pub 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; |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | 4 | ||
| 5 | /// Returns whether x is negative or negative 0. | 5 | /// Returns whether x is negative or negative 0. |
| 6 | pub fn signbit(x: var) bool { | 6 | pub 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) = +-0 | 14 | /// - sin(+-0) = +-0 |
| 15 | /// - sin(+-inf) = nan | 15 | /// - sin(+-inf) = nan |
| 16 | /// - sin(nan) = nan | 16 | /// - sin(nan) = nan |
| 17 | pub fn sin(x: var) @TypeOf(x) { | 17 | pub 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) = +-0 | 17 | /// - sinh(+-0) = +-0 |
| 18 | /// - sinh(+-inf) = +-inf | 18 | /// - sinh(+-inf) = +-inf |
| 19 | /// - sinh(nan) = nan | 19 | /// - sinh(nan) = nan |
| 20 | pub fn sinh(x: var) @TypeOf(x) { | 20 | pub 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 < 0 | 13 | /// - sqrt(x) = nan if x < 0 |
| 14 | /// - sqrt(nan) = nan | 14 | /// - 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. |
| 16 | pub fn sqrt(x: var) Sqrt(@TypeOf(x)) { | 16 | pub 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) = +-0 | 14 | /// - tan(+-0) = +-0 |
| 15 | /// - tan(+-inf) = nan | 15 | /// - tan(+-inf) = nan |
| 16 | /// - tan(nan) = nan | 16 | /// - tan(nan) = nan |
| 17 | pub fn tan(x: var) @TypeOf(x) { | 17 | pub 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) = +-0 | 17 | /// - sinh(+-0) = +-0 |
| 18 | /// - sinh(+-inf) = +-1 | 18 | /// - sinh(+-inf) = +-1 |
| 19 | /// - sinh(nan) = nan | 19 | /// - sinh(nan) = nan |
| 20 | pub fn tanh(x: var) @TypeOf(x) { | 20 | pub 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) = +-0 | 15 | /// - trunc(+-0) = +-0 |
| 16 | /// - trunc(+-inf) = +-inf | 16 | /// - trunc(+-inf) = +-inf |
| 17 | /// - trunc(nan) = nan | 17 | /// - trunc(nan) = nan |
| 18 | pub fn trunc(x: var) @TypeOf(x) { | 18 | pub 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 | } |
| 68 | 69 | ||
| 70 | fn 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 | |||
| 69 | test "math.trunc" { | 91 | test "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 | } |
| 73 | 96 | ||
| 74 | test "math.trunc32" { | 97 | test "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 | } |
| 85 | 108 | ||
| 109 | test "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 | |||
| 86 | test "math.trunc32.special" { | 115 | test "math.trunc32.special" { |
| 87 | expect(trunc32(0.0) == 0.0); // 0x3F800000 | 116 | 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 | |||
| 131 | test "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; |
| 8 | const trait = meta.trait; | 8 | const trait = meta.trait; |
| 9 | const testing = std.testing; | 9 | const testing = std.testing; |
| 10 | 10 | ||
| 11 | // https://github.com/ziglang/zig/issues/2564 | ||
| 11 | pub const page_size = switch (builtin.arch) { | 12 | pub 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) { |
| 16 | pub const Allocator = struct { | 17 | pub const Allocator = struct { |
| 17 | pub const Error = error{OutOfMemory}; | 18 | pub const Error = error{OutOfMemory}; |
| 18 | 19 | ||
| 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 allocation | 67 | /// 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 client | 72 | /// this function returns `error.OutOfMemory` when the allocator decides the client |
| 26 | /// would be better off keeping the extra alignment/size. Clients will call | 73 | /// 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 considers | 75 | /// 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 handle | 77 | /// 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 of | 84 | /// 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 to | 89 | /// 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 to | 95 | /// 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 | } | ||
| 61 | 116 | ||
| 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 to | 120 | 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 to | 123 | 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 | } | ||
| 77 | 149 | ||
| 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 { |
| 85 | 157 | ||
| 86 | /// `ptr` should be the return value of `create`, or otherwise | 158 | /// `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 | } |
| 95 | 166 | ||
| 96 | /// Allocates an array of `n` items of type `T` and sets all the | 167 | /// 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 | } |
| 146 | 217 | ||
| 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 aligned | 222 | /// 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); |
| 158 | 242 | ||
| ... | @@ -161,15 +245,19 @@ pub const Allocator = struct { | ... | @@ -161,15 +245,19 @@ pub const Allocator = struct { |
| 161 | } | 245 | } |
| 162 | 246 | ||
| 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 do | 259 | // 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 | } |
| 195 | 301 | ||
| 196 | /// This is the same as `realloc`, except caller may additionally request | 302 | /// 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 old | 303 | /// 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 failure | 324 | // 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 | } |
| 225 | 328 | ||
| 226 | /// Prefer calling realloc to shrink if you can tolerate failure, such as | 329 | /// 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; |
| 250 | 353 | ||
| 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); |
| 258 | 358 | ||
| 259 | // Here we skip the overflow checking on the multiplication because | 359 | // 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 { |
| 262 | 362 | ||
| 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 | } |
| 269 | 368 | ||
| 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 | } |
| 282 | 380 | ||
| 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 | }; |
| 298 | 396 | ||
| 397 | /// Detects and asserts if the std.mem.Allocator interface is violated by the caller | ||
| 398 | /// or the allocator. | ||
| 399 | pub 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 | |||
| 461 | pub 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`. | ||
| 469 | pub 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 | |||
| 299 | var failAllocator = Allocator{ | 480 | var failAllocator = Allocator{ |
| 300 | .reallocFn = failAllocatorRealloc, | 481 | .allocFn = failAllocatorAlloc, |
| 301 | .shrinkFn = failAllocatorShrink, | 482 | .resizeFn = Allocator.noResize, |
| 302 | }; | 483 | }; |
| 303 | fn failAllocatorRealloc(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { | 484 | fn failAllocatorAlloc(self: *Allocator, n: usize, alignment: u29, len_align: u29) Allocator.Error![]u8 { |
| 304 | return error.OutOfMemory; | 485 | return error.OutOfMemory; |
| 305 | } | 486 | } |
| 306 | fn 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 | } | ||
| 309 | 487 | ||
| 310 | test "mem.Allocator basics" { | 488 | test "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 | } |
| 343 | 521 | ||
| 522 | /// Sets all elements of `dest` to `value`. | ||
| 344 | pub fn set(comptime T: type, dest: []T, value: T) void { | 523 | pub 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 | } |
| 500 | 679 | ||
| 680 | /// Sets a slice to zeroes. | ||
| 681 | /// Prevents the store from being optimized out. | ||
| 501 | pub fn secureZero(comptime T: type, s: []T) void { | 682 | pub fn secureZero(comptime T: type, s: []T) void { |
| 502 | // NOTE: We do not use a volatile slice cast here since LLVM cannot | 683 | // 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. |
| 522 | pub fn zeroInit(comptime T: type, init: var) T { | 703 | pub fn zeroInit(comptime T: type, init: anytype) T { |
| 523 | comptime const Init = @TypeOf(init); | 704 | comptime const Init = @TypeOf(init); |
| 524 | 705 | ||
| 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); |
| 530 | 711 | ||
| 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 | } |
| 551 | 739 | ||
| ... | @@ -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 | }; |
| 577 | 765 | ||
| 578 | const s = zeroInit(S, .{ | 766 | const s = zeroInit(S, .{ |
| 579 | .a = 42, | 767 | .a = 42, |
| 580 | }); | 768 | }); |
| 581 | 769 | ||
| 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 | } |
| 592 | 795 | ||
| 796 | /// Compares two slices of numbers lexicographically. O(n). | ||
| 593 | pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order { | 797 | pub 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, the | 924 | /// 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. |
| 722 | pub fn span(ptr: var) Span(@TypeOf(ptr)) { | 926 | pub 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 array | 951 | /// Same as `span`, except when there is both a sentinel and an array |
| 748 | /// length or slice length, scans the memory for the sentinel value | 952 | /// length or slice length, scans the memory for the sentinel value |
| 749 | /// rather than using the length. | 953 | /// rather than using the length. |
| 750 | pub fn spanZ(ptr: var) Span(@TypeOf(ptr)) { | 954 | pub 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. |
| 779 | pub fn len(value: var) usize { | 983 | pub 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 array | 1028 | /// 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. |
| 827 | pub fn lenZ(ptr: var) usize { | 1031 | pub 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. |
| 1494 | pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 { | 1698 | pub 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. | ||
| 1704 | pub 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 | |||
| 1709 | fn 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{}; |
| 1496 | 1711 | ||
| 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 | }; |
| 1503 | 1718 | ||
| ... | @@ -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 | } |
| 1515 | 1730 | ||
| 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 | } |
| 1537 | 1754 | ||
| 1755 | test "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. |
| 1539 | pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T { | 1777 | pub 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 | } |
| 1729 | 1967 | ||
| 1968 | /// Returns the smallest number in a slice. O(n). | ||
| 1969 | /// `slice` must not be empty. | ||
| 1730 | pub fn min(comptime T: type, slice: []const T) T { | 1970 | pub 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 | } |
| 1741 | 1981 | ||
| 1982 | /// Returns the largest number in a slice. O(n). | ||
| 1983 | /// `slice` must not be empty. | ||
| 1742 | pub fn max(comptime T: type, slice: []const T) T { | 1984 | pub 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 | } |
| 1856 | 2098 | ||
| 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. |
| 1858 | pub fn asBytes(ptr: var) AsBytesReturnType(@TypeOf(ptr)) { | 2100 | pub 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 | } |
| 1896 | 2138 | ||
| 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. |
| 1898 | pub fn toBytes(value: var) [@sizeOf(@TypeOf(value))]u8 { | 2140 | pub fn toBytes(value: anytype) [@sizeOf(@TypeOf(value))]u8 { |
| 1899 | return asBytes(&value).*; | 2141 | return asBytes(&value).*; |
| 1900 | } | 2142 | } |
| 1901 | 2143 | ||
| ... | @@ -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 | } |
| 1930 | 2172 | ||
| 1931 | ///Given a pointer to an array of bytes, returns a pointer to a value of the specified type | 2173 | /// 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. |
| 1933 | pub fn bytesAsValue(comptime T: type, bytes: var) BytesAsValueReturnType(T, @TypeOf(bytes)) { | 2175 | pub 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 | } |
| 1936 | 2178 | ||
| ... | @@ -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 | } |
| 1973 | 2215 | ||
| 1974 | ///Given a pointer to an array of bytes, returns a value of the specified type backed by a | 2216 | /// 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. |
| 1976 | pub fn bytesToValue(comptime T: type, bytes: var) T { | 2218 | pub fn bytesToValue(comptime T: type, bytes: anytype) T { |
| 1977 | return bytesAsValue(T, bytes).*; | 2219 | return bytesAsValue(T, bytes).*; |
| 1978 | } | 2220 | } |
| 1979 | test "bytesToValue" { | 2221 | test "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 | } |
| 2003 | 2245 | ||
| 2004 | pub fn bytesAsSlice(comptime T: type, bytes: var) BytesAsSliceReturnType(T, @TypeOf(bytes)) { | 2246 | pub fn bytesAsSlice(comptime T: type, bytes: anytype) BytesAsSliceReturnType(T, @TypeOf(bytes)) { |
| 2005 | // let's not give an undefined pointer to @ptrCast | 2247 | // let's not give an undefined pointer to @ptrCast |
| 2006 | // it may be equal to zero and fail a null check | 2248 | // 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 | } |
| 2082 | 2324 | ||
| 2083 | pub fn sliceAsBytes(slice: var) SliceAsBytesReturnType(@TypeOf(slice)) { | 2325 | pub fn sliceAsBytes(slice: anytype) SliceAsBytesReturnType(@TypeOf(slice)) { |
| 2084 | const Slice = @TypeOf(slice); | 2326 | const Slice = @TypeOf(slice); |
| 2085 | 2327 | ||
| 2086 | // let's not give an undefined pointer to @ptrCast | 2328 | // 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 | } |
| 2192 | 2434 | ||
| 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. | ||
| 2437 | pub 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 address | 2444 | /// 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. |
| 2195 | pub fn alignBackward(addr: usize, alignment: usize) usize { | 2446 | pub 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 | } |
| 2208 | 2459 | ||
| 2460 | /// Returns whether `alignment` is a valid alignment, meaning it is | ||
| 2461 | /// a positive power of 2. | ||
| 2462 | pub fn isValidAlign(alignment: u29) bool { | ||
| 2463 | return @popCount(u29, alignment) == 1; | ||
| 2464 | } | ||
| 2465 | |||
| 2466 | pub 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 alignment | 2473 | /// 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. |
| 2211 | pub fn isAligned(addr: usize, alignment: usize) bool { | 2475 | pub 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; |
| 6 | const testing = std.testing; | 6 | const testing = std.testing; |
| 7 | 7 | ||
| 8 | pub const trait = @import("meta/trait.zig"); | 8 | pub const trait = @import("meta/trait.zig"); |
| 9 | pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags; | ||
| 9 | 10 | ||
| 10 | const TypeInfo = builtin.TypeInfo; | 11 | const TypeInfo = builtin.TypeInfo; |
| 11 | 12 | ||
| 12 | pub fn tagName(v: var) []const u8 { | 13 | pub 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 | } |
| 252 | 253 | ||
| 253 | pub fn declarations(comptime T: type) []TypeInfo.Declaration { | 254 | pub 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 | }; |
| 276 | 277 | ||
| 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 | } |
| 324 | 325 | ||
| 325 | pub fn fields(comptime T: type) switch (@typeInfo(T)) { | 326 | pub 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 | } |
| 431 | 432 | ||
| 432 | ///Returns the active tag of a tagged union | 433 | ///Returns the active tag of a tagged union |
| 433 | pub fn activeTag(u: var) @TagType(@TypeOf(u)) { | 434 | pub 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" { |
| 480 | 481 | ||
| 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. |
| 483 | pub fn eql(a: var, b: @TypeOf(a)) bool { | 484 | pub fn eql(a: anytype, b: @TypeOf(a)) bool { |
| 484 | const T = @TypeOf(a); | 485 | const T = @TypeOf(a); |
| 485 | 486 | ||
| 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" { |
| 627 | 628 | ||
| 628 | pub const IntToEnumError = error{InvalidEnumTag}; | 629 | pub const IntToEnumError = error{InvalidEnumTag}; |
| 629 | 630 | ||
| 630 | pub fn intToEnum(comptime Tag: type, tag_int: var) IntToEnumError!Tag { | 631 | pub 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. | ||
| 700 | pub 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 | |||
| 763 | test "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 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const meta = std.meta; | ||
| 3 | const testing = std.testing; | ||
| 4 | const mem = std.mem; | ||
| 5 | const 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. | ||
| 11 | pub 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 | |||
| 117 | test "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"); |
| 9 | 9 | ||
| 10 | pub const TraitFn = fn (type) bool; | 10 | pub const TraitFn = fn (type) bool; |
| 11 | 11 | ||
| 12 | pub fn multiTrait(comptime traits: var) TraitFn { | 12 | pub 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 | } |
| 344 | 344 | ||
| 345 | pub fn hasDecls(comptime T: type, comptime names: var) bool { | 345 | pub fn isTuple(comptime T: type) bool { |
| 346 | return is(.Struct)(T) and @typeInfo(T).Struct.is_tuple; | ||
| 347 | } | ||
| 348 | |||
| 349 | test "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 | |||
| 358 | pub 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 | } |
| 370 | 383 | ||
| 371 | pub fn hasFields(comptime T: type, comptime names: var) bool { | 384 | pub 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 | } |
| 396 | 409 | ||
| 397 | pub fn hasFunctions(comptime T: type, comptime names: var) bool { | 410 | pub 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) !* |
| 682 | 682 | ||
| 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( |
| 1404 | 1404 | ||
| 1405 | fn dnsParse( | 1405 | fn 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; |
| 304 | 308 | ||
| 305 | /// Returns the number of bytes that were read, which can be less than | 309 | /// 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 { |
| 500 | pub const TruncateError = error{ | 507 | pub 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; |
| 506 | 516 | ||
| 507 | pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { | 517 | 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,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 writing | 534 | .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 writing | 547 | wasi.EBADF => unreachable, // Handle not open for writing |
| 538 | wasi.EINVAL => unreachable, // Handle not open for writing | 548 | 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 writing | 570 | EBADF => unreachable, // Handle not open for writing |
| 560 | EINVAL => unreachable, // Handle not open for writing | 571 | 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 | } |
| 985 | 1004 | ||
| 986 | pub const OpenError = error{ | 1005 | pub 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 | } |
| 1500 | 1522 | ||
| 1501 | pub const SymLinkError = error{ | 1523 | pub 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`. |
| 1522 | pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void { | 1546 | pub 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 | } |
| 1533 | 1559 | ||
| 1534 | pub const symlinkC = @compileError("deprecated: renamed to symlinkZ"); | 1560 | pub 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 | } |
| 1563 | 1589 | ||
| 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`. | ||
| 1564 | pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { | 1596 | pub 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 | } |
| 1569 | 1609 | ||
| 1570 | pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ"); | 1610 | pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ"); |
| 1571 | 1611 | ||
| 1612 | /// WASI-only. The same as `symlinkat` but targeting WASI. | ||
| 1613 | /// See also `symlinkat`. | ||
| 1614 | pub 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`. | ||
| 1638 | pub 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`. | ||
| 1572 | pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void { | 1644 | pub 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: [*: |
| 1592 | 1669 | ||
| 1593 | pub const UnlinkError = error{ | 1670 | pub 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`. |
| 1615 | pub fn unlink(file_path: []const u8) UnlinkError!void { | 1695 | pub 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 |
| 1670 | 1752 | ||
| 1671 | pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ"); | 1753 | pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ"); |
| 1672 | 1754 | ||
| 1755 | /// WASI-only. Same as `unlinkat` but targeting WASI. | ||
| 1756 | /// See also `unlinkat`. | ||
| 1673 | pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { | 1757 | pub 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, | ||
| 1694 | 1779 | ||
| 1695 | wasi.EINVAL => unreachable, // invalid flags, or pathname has . as last component | 1780 | wasi.EINVAL => unreachable, // invalid flags, or pathname has . as last component |
| 1696 | wasi.EBADF => unreachable, // always a race condition | 1781 | 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 | } |
| 1794 | 1879 | ||
| 1795 | const RenameError = error{ | 1880 | const 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{ |
| 1816 | 1903 | ||
| 1817 | /// Change the name or location of a file. | 1904 | /// Change the name or location of a file. |
| 1818 | pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { | 1905 | pub 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 | } |
| 1889 | 1978 | ||
| 1890 | /// Same as `renameat` expect only WASI. | 1979 | /// WASI-only. Same as `renameat` expect targeting WASI. |
| 1980 | /// See also `renameat`. | ||
| 1891 | pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void { | 1981 | pub 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 | } |
| 2009 | 2100 | ||
| 2010 | pub 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 | |||
| 2027 | pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void { | 2101 | pub 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 | } |
| 2091 | 2166 | ||
| 2167 | pub 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. |
| 2094 | pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { | 2188 | pub 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{ |
| 2145 | 2241 | ||
| 2146 | /// Deletes an empty directory. | 2242 | /// Deletes an empty directory. |
| 2147 | pub fn rmdir(dir_path: []const u8) DeleteDirError!void { | 2243 | pub 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. |
| 2196 | pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { | 2294 | pub 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 | } |
| 2247 | 2347 | ||
| 2248 | pub const ReadLinkError = error{ | 2348 | pub 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. |
| 2260 | pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { | 2362 | pub 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 { |
| 2269 | 2373 | ||
| 2270 | pub const readlinkC = @compileError("deprecated: renamed to readlinkZ"); | 2374 | pub const readlinkC = @compileError("deprecated: renamed to readlinkZ"); |
| 2271 | 2375 | ||
| 2376 | /// Windows-only. Same as `readlink` expecte `file_path` is null-terminated, WTF16 encoded. | ||
| 2377 | /// Seel also `readlinkZ`. | ||
| 2378 | pub 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. |
| 2273 | pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { | 2383 | pub 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 | } |
| 2293 | 2403 | ||
| 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`. | ||
| 2407 | pub 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 | |||
| 2294 | pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ"); | 2419 | pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ"); |
| 2295 | 2420 | ||
| 2421 | /// WASI-only. Same as `readlinkat` but targets WASI. | ||
| 2422 | /// See also `readlinkat`. | ||
| 2423 | pub 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`. | ||
| 2443 | pub 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`. | ||
| 2296 | pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { | 2449 | pub 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 { |
| 2958 | 3111 | ||
| 2959 | pub const FStatError = error{ | 3112 | pub 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; |
| 2963 | 3119 | ||
| 3120 | /// Return information about a file descriptor. | ||
| 2964 | pub fn fstat(fd: fd_t) FStatError!Stat { | 3121 | pub 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 | } | ||
| 2976 | 3137 | ||
| 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 { |
| 2987 | 3148 | ||
| 2988 | pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound }; | 3149 | pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound }; |
| 2989 | 3150 | ||
| 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`. | ||
| 2990 | pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat { | 3154 | pub 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 | } |
| 2994 | 3164 | ||
| 2995 | pub const fstatatC = @compileError("deprecated: renamed to fstatatZ"); | 3165 | pub const fstatatC = @compileError("deprecated: renamed to fstatatZ"); |
| 2996 | 3166 | ||
| 3167 | /// WASI-only. Same as `fstatat` but targeting WASI. | ||
| 3168 | /// See also `fstatat`. | ||
| 3169 | pub 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`. | ||
| 2997 | pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat { | 3188 | pub 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 | } |
| 3495 | 3686 | ||
| 3496 | pub const SeekError = error{Unseekable} || UnexpectedError; | 3687 | pub 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; | ||
| 3497 | 3694 | ||
| 3498 | /// Repositions read/write file offset relative to the beginning. | 3695 | /// Repositions read/write file offset relative to the beginning. |
| 3499 | pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { | 3696 | 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,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 | } |
| 3868 | 4069 | ||
| 3869 | pub fn dl_iterate_phdr( | 4070 | pub 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; |
| 18 | const tmpDir = std.testing.tmpDir; | 18 | const tmpDir = std.testing.tmpDir; |
| 19 | const Dir = std.fs.Dir; | 19 | const Dir = std.fs.Dir; |
| 20 | 20 | ||
| 21 | test "makePath, put some files in it, deleteTree" { | 21 | test "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 | |||
| 36 | test "access file" { | ||
| 37 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 38 | |||
| 39 | var tmp = tmpDir(.{}); | ||
| 40 | defer tmp.cleanup(); | ||
| 41 | 24 | ||
| 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 | |||
| 54 | fn testThreadIdFn(thread_id: *Thread.Id) void { | ||
| 55 | thread_id.* = Thread.getCurrentId(); | ||
| 56 | } | ||
| 57 | |||
| 58 | test "sendfile" { | ||
| 59 | var tmp = tmpDir(.{}); | 25 | var tmp = tmpDir(.{}); |
| 60 | defer tmp.cleanup(); | 26 | defer tmp.cleanup(); |
| 61 | 27 | ||
| 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"; |
| 64 | 30 | 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 | }; | ||
| 80 | 31 | ||
| 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", .{}); |
| 83 | 34 | 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 | }; | ||
| 111 | 36 | ||
| 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 | } |
| 122 | 42 | ||
| 123 | test "fs.copyFile" { | 43 | test "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"; | ||
| 128 | 46 | ||
| 129 | var tmp = tmpDir(.{}); | 47 | var tmp = tmpDir(.{}); |
| 130 | defer tmp.cleanup(); | 48 | defer tmp.cleanup(); |
| 131 | 49 | ||
| 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"); |
| 134 | 52 | ||
| 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"); |
| 137 | 55 | ||
| 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; |
| 140 | 58 | 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 | } |
| 144 | 61 | ||
| 145 | fn expectFileContents(dir: Dir, file_path: []const u8, data: []const u8) !void { | 62 | fn 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 | } |
| 151 | 65 | ||
| 152 | test "std.Thread.getCurrentId" { | 66 | test "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 | } |
| 203 | 117 | ||
| 204 | test "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 | |||
| 227 | test "thread local storage" { | 118 | test "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 | } |
| 260 | 151 | ||
| 261 | test "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 | |||
| 268 | test "sigaltstack" { | 152 | test "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; |
| 270 | 154 |
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 | } |
| 907 | 913 | ||
| ... | @@ -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; |
| 593 | pub const FILE_END = 2; | 593 | pub const FILE_END = 2; |
| 594 | 594 | ||
| 595 | pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; | 595 | pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; |
| 596 | pub const HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010; | ||
| 596 | pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; | 597 | pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; |
| 597 | pub const HEAP_NO_SERIALIZE = 0x00000001; | 598 | pub const HEAP_NO_SERIALIZE = 0x00000001; |
| 598 | 599 |
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; |
| 163 | pub const IPPROTO_ICMPV6 = 58; | 163 | pub const IPPROTO_ICMPV6 = 58; |
| 164 | pub const IPPROTO_RM = 113; | 164 | pub const IPPROTO_RM = 113; |
| 165 | 165 | ||
| 166 | pub const AI_PASSIVE = 0x00001; | 166 | pub const AI_PASSIVE = 0x00001; |
| 167 | pub const AI_CANONNAME = 0x00002; | 167 | pub const AI_CANONNAME = 0x00002; |
| 168 | pub const AI_NUMERICHOST = 0x00004; | 168 | pub const AI_NUMERICHOST = 0x00004; |
| 169 | pub const AI_NUMERICSERV = 0x00008; | 169 | pub const AI_NUMERICSERV = 0x00008; |
| 170 | pub const AI_ADDRCONFIG = 0x00400; | 170 | pub const AI_ADDRCONFIG = 0x00400; |
| 171 | pub const AI_V4MAPPED = 0x00800; | 171 | pub const AI_V4MAPPED = 0x00800; |
| 172 | pub const AI_NON_AUTHORITATIVE = 0x04000; | 172 | pub const AI_NON_AUTHORITATIVE = 0x04000; |
| 173 | pub const AI_SECURE = 0x08000; | 173 | pub const AI_SECURE = 0x08000; |
| 174 | pub const AI_RETURN_PREFERRED_NAMES = 0x10000; | 174 | pub const AI_RETURN_PREFERRED_NAMES = 0x10000; |
| 175 | pub const AI_DISABLE_IDN_ENCODING = 0x80000; | 175 | pub const AI_DISABLE_IDN_ENCODING = 0x80000; |
| 176 | 176 | ||
| 177 | pub const FIONBIO = -2147195266; | 177 | pub const FIONBIO = -2147195266; |
| 178 | 178 |
lib/std/pdb.zig+1-1| ... | @@ -469,7 +469,7 @@ pub const Pdb = struct { | ... | @@ -469,7 +469,7 @@ pub const Pdb = struct { |
| 469 | 469 | ||
| 470 | msf: Msf, | 470 | msf: Msf, |
| 471 | 471 | ||
| 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" { |
| 333 | 333 | ||
| 334 | test "std.PriorityQueue: fromOwnedSlice" { | 334 | test "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(); |
| 339 | 339 |
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 geometrically | 36 | // 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 | } |
| 175 | 175 | ||
| ... | @@ -281,9 +281,6 @@ pub const ArgIteratorWasi = struct { | ... | @@ -281,9 +281,6 @@ pub const ArgIteratorWasi = struct { |
| 281 | pub const ArgIteratorWindows = struct { | 281 | pub 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, | ||
| 287 | 284 | ||
| 288 | pub const NextError = error{OutOfMemory}; | 285 | pub const NextError = error{OutOfMemory}; |
| 289 | 286 | ||
| ... | @@ -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 | } |
| 303 | 297 | ||
| ... | @@ -328,6 +322,7 @@ pub const ArgIteratorWindows = struct { | ... | @@ -328,6 +322,7 @@ pub const ArgIteratorWindows = struct { |
| 328 | } | 322 | } |
| 329 | 323 | ||
| 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(); |
| 361 | 356 | ||
| 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; |
| 371 | 367 | ||
| 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 | }; |
| 429 | 402 | ||
| 430 | pub const ArgIterator = struct { | 403 | pub 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 | } |
| 469 | 442 | ||
| ... | @@ -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" }); |
| 582 | 555 | ||
| 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 | } |
| 226 | 226 | ||
| 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 | } |
| 236 | 236 | ||
| 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 | } |
| 124 | 124 | ||
| 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 | } |
| 243 | 243 | ||
| 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" { |
| 76 | pub fn insertionSort( | 76 | pub 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 { |
| 182 | pub fn sort( | 182 | pub 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.c | 188 | // 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( |
| 1112 | fn swap( | 1112 | fn 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 { |
| 1358 | pub fn argMin( | 1358 | pub 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" { |
| 1390 | pub fn min( | 1390 | pub 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" { |
| 1410 | pub fn argMax( | 1410 | pub 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" { |
| 1442 | pub fn max( | 1442 | pub 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" { |
| 1462 | pub fn isSorted( | 1462 | pub 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 | } |
| 137 | 137 | ||
| 138 | fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { | 138 | fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void { |
| 139 | // run the build script to collect the options | 139 | // 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 | } |
| 204 | 204 | ||
| 205 | fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: var) void { | 205 | fn 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; |
| 4 | fn test__clzsi2(a: u32, expected: i32) void { | 4 | fn 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 #421 | 186 | 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 #421 | 190 | 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 { |
| 21 | 21 | ||
| 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; | ||
| 24 | 25 | ||
| 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 | |||
| 78 | pub 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 () |
| 246 | 246 | ||
| 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; |
| 3 | pub const ArrayListAlignedUnmanaged = @import("array_list.zig").ArrayListAlignedUnmanaged; | 3 | pub const ArrayListAlignedUnmanaged = @import("array_list.zig").ArrayListAlignedUnmanaged; |
| 4 | pub const ArrayListSentineled = @import("array_list_sentineled.zig").ArrayListSentineled; | 4 | pub const ArrayListSentineled = @import("array_list_sentineled.zig").ArrayListSentineled; |
| 5 | pub const ArrayListUnmanaged = @import("array_list.zig").ArrayListUnmanaged; | 5 | pub const ArrayListUnmanaged = @import("array_list.zig").ArrayListUnmanaged; |
| 6 | pub const AutoHashMap = @import("hash_map.zig").AutoHashMap; | 6 | pub const AutoHashMap = hash_map.AutoHashMap; |
| 7 | pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged; | ||
| 7 | pub const BloomFilter = @import("bloom_filter.zig").BloomFilter; | 8 | pub const BloomFilter = @import("bloom_filter.zig").BloomFilter; |
| 8 | pub const BufMap = @import("buf_map.zig").BufMap; | 9 | pub const BufMap = @import("buf_map.zig").BufMap; |
| 9 | pub const BufSet = @import("buf_set.zig").BufSet; | 10 | pub const BufSet = @import("buf_set.zig").BufSet; |
| 10 | pub const ChildProcess = @import("child_process.zig").ChildProcess; | 11 | pub const ChildProcess = @import("child_process.zig").ChildProcess; |
| 11 | pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap; | 12 | pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap; |
| 12 | pub const DynLib = @import("dynamic_library.zig").DynLib; | 13 | pub const DynLib = @import("dynamic_library.zig").DynLib; |
| 13 | pub const HashMap = @import("hash_map.zig").HashMap; | 14 | pub const HashMap = hash_map.HashMap; |
| 15 | pub const HashMapUnmanaged = hash_map.HashMapUnmanaged; | ||
| 14 | pub const Mutex = @import("mutex.zig").Mutex; | 16 | pub const Mutex = @import("mutex.zig").Mutex; |
| 15 | pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray; | 17 | pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray; |
| 16 | pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian; | 18 | pub 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; |
| 22 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; | 24 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; |
| 23 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; | 25 | pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; |
| 24 | pub const SpinLock = @import("spinlock.zig").SpinLock; | 26 | pub const SpinLock = @import("spinlock.zig").SpinLock; |
| 25 | pub const StringHashMap = @import("hash_map.zig").StringHashMap; | 27 | pub const StringHashMap = hash_map.StringHashMap; |
| 28 | pub const StringHashMapUnmanaged = hash_map.StringHashMapUnmanaged; | ||
| 26 | pub const TailQueue = @import("linked_list.zig").TailQueue; | 29 | pub const TailQueue = @import("linked_list.zig").TailQueue; |
| 27 | pub const Target = @import("target.zig").Target; | 30 | pub const Target = @import("target.zig").Target; |
| 28 | pub const Thread = @import("thread.zig").Thread; | 31 | pub 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"); |
| 49 | pub const http = @import("http.zig"); | 52 | pub const http = @import("http.zig"); |
| 50 | pub const io = @import("io.zig"); | 53 | pub const io = @import("io.zig"); |
| 51 | pub const json = @import("json.zig"); | 54 | pub const json = @import("json.zig"); |
| 55 | pub const log = @import("log.zig"); | ||
| 52 | pub const macho = @import("macho.zig"); | 56 | pub const macho = @import("macho.zig"); |
| 53 | pub const math = @import("math.zig"); | 57 | pub const math = @import("math.zig"); |
| 54 | pub const mem = @import("mem.zig"); | 58 | pub 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 | }; |
| 105 | 130 | ||
| 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 | }; |
| 414 | 440 | ||
| 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), |
| 889 | 915 | ||
| 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 | }; |
| 894 | 929 | ||
| 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. | ||
| 1512 | pub 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. | ||
| 1283 | pub 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. | ||
| 2152 | pub 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. | ||
| 2123 | pub 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. | ||
| 71 | pub 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. | ||
| 305 | pub 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. | ||
| 531 | pub 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. | ||
| 69 | pub 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. | ||
| 294 | pub 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. | ||
| 951 | pub 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. | ||
| 310 | pub 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. | ||
| 455 | pub 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. | ||
| 539 | pub 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. | ||
| 111 | pub 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. | ||
| 2950 | pub 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 |
| 11 | pub const failing_allocator = &failing_allocator_instance.allocator; | 11 | pub const failing_allocator = &failing_allocator_instance.allocator; |
| 12 | pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0); | 12 | pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0); |
| 13 | 13 | ||
| 14 | pub var base_allocator_instance = std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]); | 14 | pub var base_allocator_instance = std.mem.validationWrap(std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..])); |
| 15 | var allocator_mem: [2 * 1024 * 1024]u8 = undefined; | 15 | var allocator_mem: [2 * 1024 * 1024]u8 = undefined; |
| 16 | 16 | ||
| 17 | /// TODO https://github.com/ziglang/zig/issues/5738 | ||
| 18 | pub var log_level = std.log.Level.warn; | ||
| 19 | |||
| 17 | /// This function is intended to be used only in tests. It prints diagnostics to stderr | 20 | /// 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. |
| 19 | pub fn expectError(expected_error: anyerror, actual_error_union: var) void { | 22 | pub 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. |
| 36 | pub fn expectEqual(expected: var, actual: @TypeOf(expected)) void { | 39 | pub 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() catch | 219 | 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(".") orelse | 221 | 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"); |
| 220 | 223 | ||
| 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 | } |
| 47 | 47 | ||
| 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 | } |
| 72 | 59 | ||
| 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 | } |
| 23 | 23 | ||
| 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 | } |
| 32 | 30 | ||
| 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 | } |
| 43 | 44 | ||
| 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)) T | 143 | /// fn startFn(@TypeOf(context)) T |
| 144 | /// where T is u8, noreturn, void, or !void | 144 | /// where T is u8, noreturn, void, or !void |
| 145 | /// caller must call wait on the returned thread | 145 | /// 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 bound | 148 | // TODO compile-time call graph analysis to determine stack upper bound |
| 149 | // https://github.com/ziglang/zig/issues/157 | 149 | // 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 | }; |
| 239 | 255 | ||
| 240 | pub const Utf16LeIterator = struct { | 256 | pub 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 | } |
| 453 | 469 | ||
| 470 | test "utf8 iterator peeking" { | ||
| 471 | comptime testUtf8Peeking(); | ||
| 472 | testUtf8Peeking(); | ||
| 473 | } | ||
| 474 | |||
| 475 | fn 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 | |||
| 454 | fn testError(bytes: []const u8, expected_err: anyerror) void { | 495 | fn 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 @@ |
| 1 | const std = @import("std.zig"); | ||
| 1 | const tokenizer = @import("zig/tokenizer.zig"); | 2 | const tokenizer = @import("zig/tokenizer.zig"); |
| 3 | |||
| 2 | pub const Token = tokenizer.Token; | 4 | pub const Token = tokenizer.Token; |
| 3 | pub const Tokenizer = tokenizer.Tokenizer; | 5 | pub const Tokenizer = tokenizer.Tokenizer; |
| 4 | pub const parse = @import("zig/parse.zig").parse; | 6 | pub 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"); |
| 9 | pub const system = @import("zig/system.zig"); | 11 | pub const system = @import("zig/system.zig"); |
| 10 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; | 12 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; |
| 11 | 13 | ||
| 14 | pub 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. | ||
| 18 | pub 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 | |||
| 12 | pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { | 29 | pub 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 | } |
| 28 | 45 | ||
| 46 | /// Returns the standard file system basename of a binary generated by the Zig compiler. | ||
| 47 | pub 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 | |||
| 29 | test "" { | 67 | test "" { |
| 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 | } |
| 31 | 31 | ||
| 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 | } |
| 35 | 35 | ||
| ... | @@ -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, |
| 169 | 169 | ||
| 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, |
| 324 | 324 | ||
| 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, |
| 334 | 334 | ||
| 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 | }; |
| 340 | 340 | ||
| ... | @@ -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, |
| 344 | 344 | ||
| 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, |
| 362 | 362 | ||
| 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) { |
| 375 | 375 | ||
| 376 | token: TokenIndex, | 376 | token: TokenIndex, |
| 377 | 377 | ||
| 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) { |
| 388 | 388 | ||
| 389 | token: TokenIndex, | 389 | token: TokenIndex, |
| 390 | 390 | ||
| 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 | }; |
| 397 | 397 | ||
| 398 | pub const Node = struct { | 398 | pub const Node = struct { |
| 399 | id: Id, | 399 | tag: Tag, |
| 400 | 400 | ||
| 401 | pub const Id = enum { | 401 | pub const Tag = enum { |
| 402 | // Top level | 402 | // 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, |
| 410 | 410 | ||
| 411 | // Operators | 411 | // 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, some | 474 | /// 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, |
| 435 | 495 | ||
| 436 | // Type expressions | 496 | // 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 | }; |
| 475 | 655 | ||
| 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 | } |
| 482 | 679 | ||
| 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 | } |
| 492 | 689 | ||
| 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 | } |
| 502 | 699 | ||
| 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 | } |
| 545 | 733 | ||
| 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 | } |
| 554 | 742 | ||
| 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 | } |
| 563 | 751 | ||
| 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 | } |
| 584 | 772 | ||
| 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)}); |
| 604 | 792 | ||
| 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 { |
| 610 | 798 | ||
| 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, |
| 616 | 804 | ||
| ... | @@ -662,42 +850,84 @@ pub const Node = struct { | ... | @@ -662,42 +850,84 @@ pub const Node = struct { |
| 662 | } | 850 | } |
| 663 | }; | 851 | }; |
| 664 | 852 | ||
| 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, |
| 681 | 861 | ||
| 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; |
| 684 | 914 | ||
| 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 | } |
| 689 | 919 | ||
| 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 | } |
| 694 | 924 | ||
| 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 | } |
| 699 | 929 | ||
| 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 | } |
| 707 | 937 | ||
| 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 | } |
| 716 | 946 | ||
| 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 | }; |
| 721 | 955 | ||
| 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 | }; |
| 748 | 982 | ||
| 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 { |
| 797 | 1031 | ||
| 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 | }; |
| 867 | 1101 | ||
| 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 | }; |
| 918 | 1152 | ||
| 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, |
| 923 | 1157 | ||
| ... | @@ -942,7 +1176,7 @@ pub const Node = struct { | ... | @@ -942,7 +1176,7 @@ pub const Node = struct { |
| 942 | }; | 1176 | }; |
| 943 | 1177 | ||
| 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, |
| 947 | 1181 | ||
| 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 | }; |
| 960 | 1194 | ||
| 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 declaration | 1206 | body_node: *Node, |
| 974 | align_expr: ?*Node, // populated if align(A) is present | 1207 | lib_name: *Node, // populated if this is an extern declaration |
| 975 | section_expr: ?*Node, // populated if linksection(A) is present | 1208 | align_expr: *Node, // populated if align(A) is present |
| 976 | callconv_expr: ?*Node, // populated if callconv(A) is present | 1209 | section_expr: *Node, // populated if linksection(A) is present |
| 977 | is_extern_prototype: bool = false, // TODO: Remove once extern fn rewriting is | 1210 | callconv_expr: *Node, // populated if callconv(A) is present |
| 978 | is_async: bool = false, // TODO: remove once async fn rewriting is | 1211 | 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 | }; | ||
| 979 | 1224 | ||
| 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, |
| 992 | 1237 | ||
| 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 | }; |
| 998 | 1242 | ||
| ... | @@ -1001,8 +1245,7 @@ pub const Node = struct { | ... | @@ -1001,8 +1245,7 @@ pub const Node = struct { |
| 1001 | 1245 | ||
| 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 | } |
| 1022 | 1264 | ||
| 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 | }; |
| 1030 | 1271 | ||
| 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 | } |
| 1036 | 1324 | ||
| 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 | } |
| 1041 | 1329 | ||
| 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; |
| 1044 | 1332 | ||
| 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 | 0 | 1339 | 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; |
| 1064 | 1350 | ||
| 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 | } |
| 1069 | 1355 | ||
| 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 | } |
| 1082 | 1368 | ||
| 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 | } |
| 1090 | 1376 | ||
| 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 | } |
| 1097 | 1383 | ||
| 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 | } |
| 1105 | 1391 | ||
| 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 | } |
| 1110 | 1396 | ||
| 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 | } |
| 1115 | 1401 | ||
| 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 | }; |
| 1120 | 1406 | ||
| 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, |
| 1125 | 1411 | ||
| ... | @@ -1151,7 +1437,7 @@ pub const Node = struct { | ... | @@ -1151,7 +1437,7 @@ pub const Node = struct { |
| 1151 | 1437 | ||
| 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 | }; |
| 1206 | 1492 | ||
| 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 | }; |
| 1230 | 1516 | ||
| 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 | }; |
| 1254 | 1540 | ||
| 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, |
| 1259 | 1545 | ||
| ... | @@ -1276,7 +1562,7 @@ pub const Node = struct { | ... | @@ -1276,7 +1562,7 @@ pub const Node = struct { |
| 1276 | }; | 1562 | }; |
| 1277 | 1563 | ||
| 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 | }; |
| 1301 | 1587 | ||
| 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 | }; |
| 1326 | 1612 | ||
| 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 | }; |
| 1357 | 1643 | ||
| 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 { |
| 1441 | 1727 | ||
| 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 | }; |
| 1500 | 1786 | ||
| 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, |
| 1504 | 1790 | ||
| 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 | }; |
| 1517 | 1803 | ||
| 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 | }; |
| 1576 | 1862 | ||
| 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 | }; |
| 1627 | 1913 | ||
| 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 | }; |
| 1670 | 1956 | ||
| 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, | ||
| 1677 | 1963 | ||
| 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; |
| 1726 | 1966 | ||
| 1727 | if (i < 1) return self.lhs; | 1967 | if (i < 1) return self.lhs; |
| 1728 | i -= 1; | 1968 | i -= 1; |
| 1729 | 1969 | ||
| 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 | } |
| 1782 | 1974 | ||
| 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 | } |
| 1788 | 1980 | ||
| 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 | } |
| 1792 | 1984 | ||
| 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 | }; |
| 1797 | 1989 | ||
| 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, |
| 1803 | 1995 | ||
| 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 | }; | ||
| 1818 | 1998 | ||
| 1819 | pub const ArrayInfo = struct { | 1999 | if (i < 1) return self.lhs; |
| 1820 | len_expr: *Node, | 2000 | i -= 1; |
| 1821 | sentinel: ?*Node, | ||
| 1822 | }; | ||
| 1823 | 2001 | ||
| 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 | |
| 1830 | 2008 | 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 | |
| 1834 | 2012 | 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 | } | ||
| 1841 | 2028 | ||
| 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; |
| 1844 | 2046 | ||
| 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 | } | ||
| 1851 | 2049 | ||
| 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 | }, | ||
| 1857 | 2052 | ||
| 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 | }, | ||
| 1866 | 2055 | ||
| 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 | } |
| 1878 | 2116 | ||
| 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 | } |
| 1884 | 2122 | ||
| 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 | } |
| 1888 | 2126 | ||
| 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 | }; |
| 1893 | 2167 | ||
| 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 { |
| 1917 | 2191 | ||
| 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 { |
| 1970 | 2244 | ||
| 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 { |
| 2020 | 2294 | ||
| 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 { |
| 2073 | 2347 | ||
| 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 { |
| 2123 | 2397 | ||
| 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 | }; |
| 2178 | 2452 | ||
| 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 | }; |
| 2238 | 2512 | ||
| 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 | }; |
| 2262 | 2536 | ||
| 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 | }; |
| 2317 | 2593 | ||
| 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, |
| 2322 | 2598 | ||
| ... | @@ -2345,7 +2621,7 @@ pub const Node = struct { | ... | @@ -2345,7 +2621,7 @@ pub const Node = struct { |
| 2345 | }; | 2621 | }; |
| 2346 | 2622 | ||
| 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, |
| 2350 | 2626 | ||
| 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 | }; |
| 2363 | 2639 | ||
| 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, |
| 2368 | 2644 | ||
| ... | @@ -2380,7 +2656,7 @@ pub const Node = struct { | ... | @@ -2380,7 +2656,7 @@ pub const Node = struct { |
| 2380 | }; | 2656 | }; |
| 2381 | 2657 | ||
| 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, |
| 2385 | 2661 | ||
| 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 { |
| 2398 | 2674 | ||
| 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 | }; |
| 2448 | 2724 | ||
| 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, |
| 2452 | 2728 | ||
| 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 { |
| 2465 | 2741 | ||
| 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, |
| 2470 | 2746 | ||
| 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 | }; |
| 2508 | 2784 | ||
| 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, |
| 2512 | 2788 | ||
| 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 | }; |
| 2525 | 2801 | ||
| 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, |
| 2529 | 2805 | ||
| 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 | }; |
| 2542 | 2818 | ||
| 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, |
| 2546 | 2822 | ||
| 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 | }; |
| 2559 | 2835 | ||
| 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, |
| 2563 | 2839 | ||
| 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 | }; |
| 2576 | 2852 | ||
| 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 | }; |
| 2696 | 2972 | ||
| 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, |
| 2700 | 2976 | ||
| 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 | }; |
| 2713 | 2989 | ||
| 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, |
| 2717 | 2993 | ||
| 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 | }; |
| 2730 | 3006 | ||
| 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, |
| 2734 | 3010 | ||
| 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 | } |
| 2738 | 3014 | ||
| 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 | } |
| 2742 | 3018 | ||
| 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 | }; |
| 2747 | 3023 | ||
| 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 the | 3029 | /// 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 stopping | 3030 | /// 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 | }; |
| 2769 | 3048 | ||
| 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 | }; |
| 2795 | 3074 | ||
| 3075 | pub 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 | |||
| 2796 | test "iterate" { | 3093 | test "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 { |
| 497 | 497 | ||
| 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 | } |
| 502 | 502 | ||
| 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 | } |
| 527 | 527 |
lib/std/zig/parse.zig+415-255| ... | @@ -150,7 +150,7 @@ const Parser = struct { | ... | @@ -150,7 +150,7 @@ const Parser = struct { |
| 150 | 150 | ||
| 151 | const visib_token = p.eatToken(.Keyword_pub); | 151 | const visib_token = p.eatToken(.Keyword_pub); |
| 152 | 152 | ||
| 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 | } |
| 189 | 166 | ||
| ... | @@ -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? VarDecl | 395 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl |
| 419 | /// / KEYWORD_usingnamespace Expr SEMICOLON | 396 | /// / 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 | }; |
| 432 | 409 | ||
| 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, |
| 438 | 415 | })) |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 | } |
| 449 | 418 | ||
| ... | @@ -460,12 +429,13 @@ const Parser = struct { | ... | @@ -460,12 +429,13 @@ const Parser = struct { |
| 460 | 429 | ||
| 461 | const thread_local_token = p.eatToken(.Keyword_threadlocal); | 430 | const thread_local_token = p.eatToken(.Keyword_threadlocal); |
| 462 | 431 | ||
| 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 | } |
| 471 | 441 | ||
| ... | @@ -485,21 +455,41 @@ const Parser = struct { | ... | @@ -485,21 +455,41 @@ const Parser = struct { |
| 485 | return error.ParseError; | 455 | return error.ParseError; |
| 486 | } | 456 | } |
| 487 | 457 | ||
| 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 | } |
| 490 | 475 | ||
| 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 is | 483 | // 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); |
| 521 | 512 | ||
| 522 | const return_type_expr = (try p.parseVarType()) orelse | 513 | 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 | else | 526 | else |
| 536 | R{ .Explicit = return_type_expr.? }; | 527 | R{ .Explicit = return_type_expr.? }; |
| 537 | 528 | ||
| 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 | } else | 532 | 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 | }; | ||
| 543 | 543 | ||
| 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); |
| 563 | 563 | ||
| 564 | return &fn_proto_node.base; | 564 | return &fn_proto_node.base; |
| 565 | } | 565 | } |
| 566 | 566 | ||
| 567 | /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON | 567 | /// 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) orelse | 576 | const mut_token = p.eatToken(.Keyword_const) orelse |
| 570 | p.eatToken(.Keyword_var) orelse | 577 | 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); |
| 589 | 596 | ||
| 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 | } |
| 609 | 618 | ||
| ... | @@ -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); |
| 665 | 674 | ||
| 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 | } |
| 672 | 680 | ||
| ... | @@ -937,7 +945,6 @@ const Parser = struct { | ... | @@ -937,7 +945,6 @@ const Parser = struct { |
| 937 | return node; | 945 | return node; |
| 938 | } | 946 | } |
| 939 | 947 | ||
| 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; |
| 1400 | 1407 | ||
| 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 | }); |
| 1434 | 1441 | ||
| 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; |
| 1464 | 1517 | ||
| 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; |
| 1555 | 1653 | ||
| 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 | } |
| 1655 | 1753 | ||
| 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 | } |
| 2024 | 2122 | ||
| 2025 | /// ParamType | 2123 | /// ParamType |
| 2026 | /// <- KEYWORD_var | 2124 | /// <- Keyword_anytype |
| 2027 | /// / DOT3 | 2125 | /// / DOT3 |
| 2028 | /// / TypeExpr | 2126 | /// / 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 broken | 2128 | // 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 | }); |
| 2233 | 2330 | ||
| 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 | /// / EQUAL | 2357 | /// / 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 | }; |
| 2283 | 2380 | ||
| 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 caller | 2385 | .lhs = undefined, // set by caller |
| 2288 | .op = op, | ||
| 2289 | .rhs = undefined, // set by caller | 2386 | .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 | /// / RARROWEQUAL | 2397 | /// / 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 | /// / RARROW2 | 2451 | /// / 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 | /// / MINUSPERCENT | 2471 | /// / 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 | /// / ASTERISKPERCENT | 2495 | /// / 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_await | 2521 | /// / 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 | } | ||
| 2430 | 2538 | ||
| 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 caller | 2544 | .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 caller | 2567 | .rhs = undefined, // set by caller |
| 2459 | }; | 2568 | }; |
| 2460 | return &node.base; | 2569 | return &node.base; |
| 2461 | } | 2570 | } |
| 2462 | 2571 | ||
| 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.PtrType | 2593 | &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 | else | 2598 | else |
| 2490 | &prefix_op.op.PtrType; | 2599 | unreachable; |
| 2491 | 2600 | ||
| 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 | }); |
| 2508 | 2617 | ||
| 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 | } |
| 2522 | 2631 | ||
| 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 | } |
| 2565 | 2674 | ||
| 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 duplicates | 2679 | 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 { |
| 2669 | 2774 | ||
| 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 should | 2778 | // 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 caller | 2784 | .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); |
| 2731 | 2836 | ||
| 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 | else | 2856 | } |
| 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 | }; | ||
| 2749 | 2857 | ||
| 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 caller | 2861 | .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 | else | 2878 | 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 caller | 2883 | .rhs = undefined, // set by caller |
| 2884 | .ptr_info = .{ .sentinel = sentinel }, | ||
| 2777 | }; | 2885 | }; |
| 2778 | return &node.base; | 2886 | return &node.base; |
| 2779 | } | 2887 | } |
| 2780 | 2888 | ||
| 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 caller | 2893 | .rhs = undefined, // set by caller |
| 2787 | }; | 2894 | }; |
| 2788 | 2895 | ||
| 2789 | // Special case for **, which is its own token | 2896 | // 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 caller | 2900 | .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 caller | 2921 | .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 | else | 2930 | 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 caller | 2936 | .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 { |
| 2956 | 3061 | ||
| 2957 | const NodeParseFn = fn (p: *Parser) Error!?*Node; | 3062 | const NodeParseFn = fn (p: *Parser) Error!?*Node; |
| 2958 | 3063 | ||
| 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 | } |
| 2985 | 3090 | ||
| 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; |
| 2999 | 3104 | ||
| 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 caller | 3109 | .lhs = undefined, // set by caller |
| 3004 | .op = op, | ||
| 3005 | .rhs = undefined, // set by caller | 3110 | .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 | } |
| 3060 | 3165 | ||
| 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 { |
| 3146 | 3251 | ||
| 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 caller | 3258 | .rhs = undefined, // set by caller |
| 3154 | }; | 3259 | }; |
| 3155 | return &node.base; | 3260 | return &node.base; |
| 3156 | } | 3261 | } |
| 3157 | 3262 | ||
| 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 | } |
| 3224 | 3314 | ||
| 3225 | /// Op* Child | 3315 | /// 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 nodes | 3356 | // 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 | } |
| 3253 | 3376 | ||
| 3254 | // If any prefix op existed, a child node on the RHS is required | 3377 | // 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; |
| 3297 | 3453 | ||
| 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 | } | ||
| 3301 | 3461 | ||
| 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 | } |
| 3310 | 3470 | ||
| 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 caller | 3476 | .lhs = undefined, // set by caller |
| 3316 | .op = op, | ||
| 3317 | .rhs = undefined, // set by caller | 3477 | .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 @@ |
| 1 | const builtin = @import("builtin"); | 1 | test "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 | |||
| 17 | test "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 | } | ||
| 2 | 30 | ||
| 3 | test "recovery: top level" { | 31 | test "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 | } |
| 424 | 452 | ||
| 425 | test "zig fmt: var struct field" { | 453 | test "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" { |
| 1932 | test "zig fmt: return types" { | 1960 | test "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 | } |
| 2142 | 2170 | ||
| 2143 | test "zig fmt: var type" { | 2171 | test "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 | } |
| 3148 | 3176 | ||
| 3149 | test "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 | |||
| 3163 | test "zig fmt: convert async fn into callconv(.Async)" { | 3177 | test "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 | } |
| 3182 | 3196 | ||
| 3197 | test "zig fmt: C var args" { | ||
| 3198 | try testCanonical( | ||
| 3199 | \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; | ||
| 3200 | \\ | ||
| 3201 | ); | ||
| 3202 | } | ||
| 3203 | |||
| 3183 | const std = @import("std"); | 3204 | const std = @import("std"); |
| 3184 | const mem = std.mem; | 3205 | const mem = std.mem; |
| 3185 | const warn = std.debug.warn; | 3206 | const 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 | }; |
| 13 | 13 | ||
| 14 | /// Returns whether anything changed | 14 | /// Returns whether anything changed |
| 15 | pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool { | 15 | pub fn render(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree) (@TypeOf(stream).Error || Error)!bool { |
| 16 | // cannot render an invalid tree | 16 | // cannot render an invalid tree |
| 17 | std.debug.assert(tree.errors.len == 0); | 17 | std.debug.assert(tree.errors.len == 0); |
| 18 | 18 | ||
| ... | @@ -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( |
| 64 | 64 | ||
| 65 | fn renderRoot( | 65 | fn 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 file | 70 | // 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 | } |
| 193 | 193 | ||
| 194 | fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) @TypeOf(stream).Error!void { | 194 | fn 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 | } |
| 197 | 197 | ||
| 198 | fn renderExtraNewlineToken( | 198 | fn 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 | } |
| 220 | 220 | ||
| 221 | fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@TypeOf(stream).Error || Error)!void { | 221 | fn 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 | } |
| 224 | 224 | ||
| 225 | fn renderContainerDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Error || Error)!void { | 225 | fn 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); |
| 229 | 229 | ||
| 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); |
| 231 | 231 | ||
| 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); |
| 254 | 254 | ||
| 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 | }, |
| 258 | 258 | ||
| 259 | .TestDecl => { | 259 | .TestDecl => { |
| 260 | const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); | 260 | const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); |
| 261 | 261 | ||
| 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); |
| 270 | 270 | ||
| 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); // comptime | 273 | 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, |
| 358 | 358 | ||
| 359 | fn renderExpression( | 359 | fn 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 | }, |
| 438 | 438 | ||
| 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); |
| 441 | 441 | ||
| 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); |
| 447 | 444 | ||
| 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 | } |
| 460 | 457 | ||
| 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 | } |
| 467 | 461 | ||
| 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 | }, |
| 470 | 464 | ||
| 471 | .PrefixOp => { | 465 | .Add, |
| 472 | const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); | 466 | .AddWrap, |
| 473 | 467 | .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 | else | 475 | .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); // allowzero | 494 | .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); | ||
| 505 | 515 | ||
| 506 | try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align | 516 | 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 | }; | ||
| 508 | 520 | ||
| 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 | } | ||
| 510 | 528 | ||
| 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()); | ||
| 514 | 531 | ||
| 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 | }, | ||
| 519 | 543 | ||
| 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 | }, | ||
| 534 | 552 | ||
| 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 | }, | ||
| 545 | 583 | ||
| 546 | if (ptr_info.allowzero_token) |allowzero_token| { | 584 | .PtrType => { |
| 547 | try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero | 585 | 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); | ||
| 552 | 615 | ||
| 553 | try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align | 616 | 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); // ( |
| 555 | 618 | ||
| 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); |
| 557 | 620 | ||
| 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()); |
| 561 | 624 | ||
| 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); |
| 566 | 629 | ||
| 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 | }, | ||
| 581 | 645 | ||
| 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 | else | 650 | 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 | } | ||
| 588 | 657 | ||
| 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); | ||
| 590 | 664 | ||
| 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 | }, | ||
| 618 | 667 | ||
| 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 | }, | ||
| 624 | 669 | ||
| 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); | ||
| 629 | 678 | ||
| 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 | }, |
| 632 | 694 | ||
| 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 | } |
| 768 | 830 | ||
| 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).?; |
| 853 | 915 | ||
| 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( |
| 948 | 1010 | ||
| 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 fields | 1320 | // 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); |
| 1379 | 1447 | ||
| 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); // name | 1449 | return renderToken(tree, stream, tag.name_token, indent, start_col, space); // name |
| 1382 | }, | 1450 | }, |
| 1383 | 1451 | ||
| ... | @@ -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); |
| 1453 | 1521 | ||
| 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); |
| 1457 | 1525 | ||
| 1458 | try renderToken(tree, stream, visib_token_index, indent, start_col, Space.Space); // pub | 1526 | try renderToken(tree, stream, visib_token_index, indent, start_col, Space.Space); // pub |
| 1459 | } | 1527 | } |
| 1460 | 1528 | ||
| 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/inline | 1531 | try renderToken(tree, stream, extern_export_inline_token, indent, start_col, Space.Space); // extern/export/inline |
| 1464 | } | 1532 | } |
| 1465 | 1533 | ||
| 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 | } |
| 1469 | 1537 | ||
| 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); // fn | 1539 | 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); // name | 1540 | 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 left | 1549 | // the first token for the annotation expressions is the left |
| 1482 | // parenthesis, hence the need for two prevToken | 1550 | // 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); |
| 1507 | 1575 | ||
| 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 line | 1585 | // 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 | } |
| 1524 | 1599 | ||
| 1525 | try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // ) | 1600 | try renderToken(tree, stream, rparen, indent, start_col, Space.Space); // ) |
| 1526 | 1601 | ||
| 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 | } |
| 1537 | 1612 | ||
| 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 | } |
| 1548 | 1623 | ||
| 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 | } |
| 1563 | 1638 | ||
| ... | @@ -1792,7 +1867,7 @@ fn renderExpression( | ... | @@ -1792,7 +1867,7 @@ fn renderExpression( |
| 1792 | 1867 | ||
| 1793 | const rparen = tree.nextToken(for_node.array_expr.lastToken()); | 1868 | const rparen = tree.nextToken(for_node.array_expr.lastToken()); |
| 1794 | 1869 | ||
| 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; |
| 1798 | 1873 | ||
| ... | @@ -1835,7 +1910,7 @@ fn renderExpression( | ... | @@ -1835,7 +1910,7 @@ fn renderExpression( |
| 1835 | 1910 | ||
| 1836 | try renderExpression(allocator, stream, tree, indent, start_col, if_node.condition, Space.None); // condition | 1911 | try renderExpression(allocator, stream, tree, indent, start_col, if_node.condition, Space.None); // condition |
| 1837 | 1912 | ||
| 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); |
| 1840 | 1915 | ||
| 1841 | if (body_is_if_block) { | 1916 | if (body_is_if_block) { |
| ... | @@ -1939,7 +2014,7 @@ fn renderExpression( | ... | @@ -1939,7 +2014,7 @@ fn renderExpression( |
| 1939 | 2014 | ||
| 1940 | const indent_once = indent + indent_delta; | 2015 | const indent_once = indent + indent_delta; |
| 1941 | 2016 | ||
| 1942 | if (asm_node.template.id == .MultilineStringLiteral) { | 2017 | if (asm_node.template.tag == .MultilineStringLiteral) { |
| 1943 | // After rendering a multiline string literal the cursor is | 2018 | // After rendering a multiline string literal the cursor is |
| 1944 | // already offset by indent | 2019 | // 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 | } |
| 2053 | 2128 | ||
| 2129 | fn 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 | |||
| 2054 | fn renderAsmOutput( | 2169 | fn 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( |
| 2081 | 2196 | ||
| 2082 | fn renderAsmInput( | 2197 | fn 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( |
| 2099 | 2214 | ||
| 2100 | fn renderVarDecl( | 2215 | fn 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); // pub | 2224 | try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub |
| 2110 | } | 2225 | } |
| 2111 | 2226 | ||
| 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); // extern | 2228 | try renderToken(tree, stream, extern_export_token, indent, start_col, Space.Space); // extern |
| 2114 | 2229 | ||
| 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 | } |
| 2119 | 2234 | ||
| 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); // comptime | 2236 | try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space); // comptime |
| 2122 | } | 2237 | } |
| 2123 | 2238 | ||
| 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); // threadlocal | 2240 | 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); // var | 2242 | try renderToken(tree, stream, var_decl.mut_token, indent, start_col, Space.Space); // var |
| 2128 | 2243 | ||
| 2129 | const name_space = if (var_decl.type_node == null and (var_decl.align_node != null or | 2244 | 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); |
| 2132 | 2252 | ||
| 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 or | 2255 | const s = if (var_decl.getTrailer("align_node") != null or |
| 2136 | var_decl.section_node != null or | 2256 | 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 | } |
| 2140 | 2260 | ||
| 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); // align | 2265 | 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 | } |
| 2151 | 2271 | ||
| 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); // linksection | 2276 | 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 | } |
| 2162 | 2282 | ||
| 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 | } |
| 2168 | 2288 | ||
| ... | @@ -2171,14 +2291,14 @@ fn renderVarDecl( | ... | @@ -2171,14 +2291,14 @@ fn renderVarDecl( |
| 2171 | 2291 | ||
| 2172 | fn renderParamDecl( | 2292 | fn 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); |
| 2182 | 2302 | ||
| 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 | } |
| 2198 | 2317 | ||
| 2199 | fn renderStatement( | 2318 | fn 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 { |
| 2236 | 2355 | ||
| 2237 | fn renderTokenOffset( | 2356 | fn 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( |
| 2434 | 2553 | ||
| 2435 | fn renderToken( | 2554 | fn 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( |
| 2445 | 2564 | ||
| 2446 | fn renderDocComments( | 2565 | fn 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 | } |
| 2456 | 2576 | ||
| 2457 | fn renderDocCommentsToken( | 2577 | fn 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 | } |
| 2483 | 2603 | ||
| 2484 | fn nodeIsBlock(base: *const ast.Node) bool { | 2604 | fn 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 | } |
| 2495 | 2615 | ||
| 2496 | fn nodeCausesSliceOpSpace(base: *ast.Node) bool { | 2616 | fn 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 | } |
| 2503 | 2665 | ||
| ... | @@ -2532,7 +2694,7 @@ const FindByteOutStream = struct { | ... | @@ -2532,7 +2694,7 @@ const FindByteOutStream = struct { |
| 2532 | } | 2694 | } |
| 2533 | }; | 2695 | }; |
| 2534 | 2696 | ||
| 2535 | fn copyFixingWhitespace(stream: var, slice: []const u8) @TypeOf(stream).Error!void { | 2697 | fn 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 | } |
| 126 | 126 | ||
| 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. |
| 128 | pub fn render(utf8: []const u8, out_stream: var) !void { | 128 | pub 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 | } |
| 132 | 132 | ||
| 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 | } |
| 142 | 142 | ||
| 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 | } |
| 152 | 152 | ||
| 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 | } |
| 162 | 162 | ||
| 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 | }; |
| 888 | 889 | ||
| 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"); |
| 15 | const zir = @import("zir.zig"); | 15 | const zir = @import("zir.zig"); |
| 16 | const Module = @This(); | 16 | const Module = @This(); |
| 17 | const Inst = ir.Inst; | 17 | const Inst = ir.Inst; |
| 18 | 18 | const Body = ir.Body; | |
| 19 | /// General-purpose allocator. | 19 | const ast = std.zig.ast; |
| 20 | allocator: *Allocator, | 20 | const trace = @import("tracy.zig").trace; |
| 21 | const liveness = @import("liveness.zig"); | ||
| 22 | const astgen = @import("astgen.zig"); | ||
| 23 | |||
| 24 | /// General-purpose allocator. Used for both temporary and long-term storage. | ||
| 25 | gpa: *Allocator, | ||
| 21 | /// Pointer to externally managed resource. | 26 | /// Pointer to externally managed resource. |
| 22 | root_pkg: *Package, | 27 | root_pkg: *Package, |
| 23 | /// Module owns this resource. | 28 | /// Module owns this resource. |
| 24 | root_scope: *Scope.ZIRModule, | 29 | /// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`. |
| 25 | bin_file: link.ElfFile, | 30 | root_scope: *Scope, |
| 31 | bin_file: *link.File, | ||
| 26 | bin_file_dir: std.fs.Dir, | 32 | bin_file_dir: std.fs.Dir, |
| 27 | bin_file_path: []const u8, | 33 | bin_file_path: []const u8, |
| 28 | /// It's rare for a decl to be exported, so we save memory by having a sparse map of | 34 | /// 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. |
| 31 | decl_exports: std.AutoHashMap(*Decl, []*Export), | 37 | decl_exports: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{}, |
| 38 | /// We track which export is associated with the given symbol name for quick | ||
| 39 | /// detection of symbol collisions. | ||
| 40 | symbol_exports: std.StringHashMapUnmanaged(*Export) = .{}, | ||
| 32 | /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl | 41 | /// 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 that | 42 | /// 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. |
| 36 | export_owners: std.AutoHashMap(*Decl, []*Export), | 45 | export_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. |
| 38 | decl_table: std.AutoHashMap(Decl.Hash, *Decl), | 47 | decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{}, |
| 39 | 48 | ||
| 40 | optimize_mode: std.builtin.Mode, | 49 | optimize_mode: std.builtin.Mode, |
| 41 | link_error_flags: link.ElfFile.ErrorFlags = link.ElfFile.ErrorFlags{}, | 50 | link_error_flags: link.File.ErrorFlags = .{}, |
| 42 | 51 | ||
| 43 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), | 52 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), |
| 44 | 53 | ||
| ... | @@ -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. |
| 50 | failed_decls: std.AutoHashMap(*Decl, *ErrorMsg), | 59 | failed_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. |
| 53 | failed_files: std.AutoHashMap(*Scope.ZIRModule, *ErrorMsg), | 62 | failed_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. |
| 56 | failed_exports: std.AutoHashMap(*Export, *ErrorMsg), | 65 | failed_exports: std.AutoHashMapUnmanaged(*Export, *ErrorMsg) = .{}, |
| 57 | 66 | ||
| 58 | /// Incrementing integer used to compare against the corresponding Decl | 67 | /// Incrementing integer used to compare against the corresponding Decl |
| 59 | /// field to determine whether a Decl's status applies to an ongoing update, or a | 68 | /// field to determine whether a Decl's status applies to an ongoing update, or a |
| 60 | /// previous analysis. | 69 | /// previous analysis. |
| 61 | generation: u32 = 0, | 70 | generation: u32 = 0, |
| 62 | 71 | ||
| 72 | next_anon_name_index: usize = 0, | ||
| 73 | |||
| 63 | /// Candidates for deletion. After a semantic analysis update completes, this list | 74 | /// 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. |
| 65 | deletion_set: std.ArrayListUnmanaged(*Decl) = std.ArrayListUnmanaged(*Decl){}, | 76 | deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, |
| 77 | |||
| 78 | keep_source_files_loaded: bool, | ||
| 66 | 79 | ||
| 67 | pub const WorkItem = union(enum) { | 80 | pub const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 81 | |||
| 82 | const 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 | }; |
| 73 | 90 | ||
| 74 | pub const Export = struct { | 91 | pub 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 when | 119 | /// 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`. Functions | 132 | /// 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 complete | 140 | /// 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 { |
| 148 | 171 | ||
| 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, |
| 152 | 175 | ||
| 153 | contents_hash: Hash, | 176 | contents_hash: std.zig.SrcHash, |
| 154 | 177 | ||
| 155 | /// The shallow set of other decls whose typed_value could possibly change if this Decl's | 178 | /// 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's | 181 | /// 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); | ||
| 161 | 188 | ||
| 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 | } |
| 171 | 198 | ||
| 172 | pub const Hash = [16]u8; | 199 | pub fn src(self: Decl) usize { |
| 173 | 200 | 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 | } |
| 186 | 219 | ||
| 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 of | 221 | 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 | } |
| 195 | 223 | ||
| 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 | } |
| 226 | 254 | ||
| 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 | } |
| 236 | 258 | ||
| 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 | }; |
| 247 | 263 | ||
| 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. |
| 249 | pub const Fn = struct { | 265 | pub 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_decls | 270 | /// 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 complete | 272 | /// 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 instructions | 284 | |
| 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 | }; |
| 275 | 291 | ||
| 276 | pub const Scope = struct { | 292 | pub const Scope = struct { |
| 277 | tag: Tag, | 293 | tag: Tag, |
| 278 | 294 | ||
| 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 | } |
| 295 | 316 | ||
| 296 | /// Asserts the scope has a parent which is a DeclAnalysis and | 317 | /// 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 | } |
| 305 | 329 | ||
| 306 | /// Asserts the scope has a parent which is a ZIRModule and | 330 | /// 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 | } |
| 315 | 355 | ||
| 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 | } |
| 327 | 391 | ||
| 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 | }; |
| 333 | 566 | ||
| 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 | }, |
| 357 | 590 | ||
| 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 | => {}, |
| 365 | 603 | ||
| 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 | } |
| 385 | 625 | ||
| 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 | } |
| 390 | 631 | ||
| 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 | }; |
| 396 | 668 | ||
| 397 | /// This is a temporary structure, references to it are valid only | 669 | /// 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 DeclAnalysis | 678 | /// 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 | }; |
| 408 | 688 | ||
| 409 | /// This is a temporary structure, references to it are valid only | 689 | /// 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 | }; | ||
| 418 | 697 | ||
| 419 | pub 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 | }; |
| 422 | 723 | ||
| 423 | pub const AllErrors = struct { | 724 | pub 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 | }; |
| 434 | 735 | ||
| 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 | } |
| 438 | 739 | ||
| 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 | }; |
| 467 | 769 | ||
| 468 | pub fn init(gpa: *Allocator, options: InitOptions) !Module { | 770 | pub 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 | }; | ||
| 487 | 805 | ||
| 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 | } |
| 505 | 818 | ||
| 506 | pub fn deinit(self: *Module) void { | 819 | pub 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 | } |
| 560 | 860 | ||
| 561 | fn freeExportList(allocator: *Allocator, export_list: []*Export) void { | 861 | fn 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 | } |
| 567 | 867 | ||
| 568 | pub fn target(self: Module) std.Target { | 868 | pub fn target(self: Module) std.Target { |
| 569 | return self.bin_file.options.target; | 869 | return self.bin_file.options().target; |
| 570 | } | 870 | } |
| 571 | 871 | ||
| 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. |
| 573 | pub fn update(self: *Module) !void { | 873 | pub fn update(self: *Module) !void { |
| 874 | const tracy = trace(@src()); | ||
| 875 | defer tracy.end(); | ||
| 876 | |||
| 574 | self.generation += 1; | 877 | self.generation += 1; |
| 575 | 878 | ||
| 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 | } | ||
| 587 | 899 | ||
| 588 | try self.performAllTheWork(); | 900 | try self.performAllTheWork(); |
| 589 | 901 | ||
| 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 | } |
| 598 | 910 | ||
| 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 | } |
| 604 | 915 | ||
| 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 | } |
| 608 | 925 | ||
| 609 | /// Having the file open for writing is problematic as far as executing the | 926 | /// 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 | } |
| 620 | 937 | ||
| 621 | pub fn totalErrorCount(self: *Module) usize { | 938 | pub 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 | } |
| 627 | 944 | ||
| 628 | pub fn getAllErrorsAlloc(self: *Module) !AllErrors { | 945 | pub 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(); |
| 631 | 948 | ||
| 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(); |
| 634 | 951 | ||
| 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 | } |
| 662 | 970 | ||
| 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 | } |
| 680 | 988 | ||
| 681 | const InnerError = error{ OutOfMemory, AnalysisFail }; | ||
| 682 | |||
| 683 | pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { | 989 | pub 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, |
| 688 | 995 | ||
| 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, |
| 693 | 1001 | ||
| 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 | } |
| 711 | 1023 | ||
| 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 | } | ||
| 734 | 1060 | ||
| 735 | .sema_failure, | 1061 | fn 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, | ||
| 741 | 1064 | ||
| 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 | } | ||
| 768 | 1103 | ||
| 769 | fn 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 | }; | ||
| 772 | 1122 | ||
| 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 | } | ||
| 778 | 1148 | ||
| 779 | for (dependee.dependants.items) |item| { | 1149 | fn 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 | } |
| 785 | 1348 | ||
| 786 | fn getSource(self: *Module, root_scope: *Scope.ZIRModule) ![:0]const u8 { | 1349 | fn 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 | |||
| 1362 | fn 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 | } |
| 802 | 1369 | ||
| 803 | fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { | 1370 | fn 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); |
| 807 | 1374 | ||
| 808 | const source = try self.getSource(root_scope); | 1375 | const source = try root_scope.getSource(self); |
| 809 | 1376 | ||
| 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); |
| 813 | 1380 | ||
| 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); |
| 816 | 1383 | ||
| 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 | } |
| 840 | 1407 | ||
| 841 | fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void { | 1408 | fn 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); |
| 845 | 1415 | ||
| 846 | // Here we ensure enough queue capacity to store all the decls, so that later we can use | 1416 | const source = try root_scope.getSource(self); |
| 847 | // appendAssumeCapacity. | ||
| 848 | try self.work_queue.ensureUnusedCapacity(src_module.decls.len); | ||
| 849 | 1417 | ||
| 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 | }, |
| 856 | 1446 | ||
| 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 | } | ||
| 879 | 1448 | ||
| 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); | 1453 | fn 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 confused | 1499 | 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 | |||
| 1518 | fn 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 | } |
| 911 | 1571 | ||
| 912 | fn deleteDecl(self: *Module, decl: *Decl) !void { | 1572 | fn 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); | ||
| 914 | 1578 | ||
| 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 | } |
| 944 | 1609 | ||
| 945 | /// Delete all the Export objects that are caused by this Decl. Re-analysis of | 1610 | /// 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; |
| 949 | 1614 | ||
| 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 | } |
| 969 | 1634 | 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 | } |
| 975 | 1645 | ||
| 976 | fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { | 1646 | fn 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(); | ||
| 992 | 1661 | ||
| 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}); | ||
| 995 | 1666 | ||
| 996 | try self.analyzeBody(&analysis.inner_block.base, fn_inst.positionals.body); | 1667 | try self.analyzeBody(&inner_block.base, fn_zir.body); |
| 997 | 1668 | ||
| 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 | } |
| 1004 | 1673 | ||
| 1005 | fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!void { | 1674 | fn 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 | |||
| 1683 | fn 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 | } | ||
| 1019 | 1703 | ||
| 1020 | // The exports this Decl performs will be re-discovered, so we remove them here | 1704 | fn 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 one | 1711 | ) !*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 | |||
| 1720 | fn 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(); |
| 1040 | 1726 | ||
| 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; | ||
| 1054 | 1731 | ||
| 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); |
| 1061 | 1738 | ||
| 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 | } |
| 1081 | 1760 | ||
| 1082 | // If the decl is a function, and the type is the same, we do not need | 1761 | 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 | } |
| 1102 | 1763 | ||
| 1103 | fn markOutdatedDecl(self: *Module, decl: *Decl) !void { | 1764 | fn 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 | } |
| 1111 | 1769 | ||
| 1112 | fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl { | 1770 | fn 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 | } |
| 1182 | 1777 | ||
| 1183 | /// Declares a dependency on the decl. | 1778 | /// Declares a dependency on the decl. |
| 1184 | fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl { | 1779 | fn 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, |
| 1189 | 1785 | ||
| 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, |
| 1195 | 1792 | ||
| 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 | } |
| 1203 | 1797 | ||
| 1798 | /// TODO Look into removing this function. The body is only needed for .zir files, not .zig files. | ||
| 1204 | fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { | 1799 | fn 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).?; |
| 1210 | 1805 | 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 | } |
| 1215 | 1823 | ||
| ... | @@ -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 | } |
| 1260 | 1868 | ||
| 1261 | fn analyzeExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!void { | 1869 | fn 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); | ||
| 1278 | 1882 | ||
| 1279 | const owner_decl = scope.decl().?; | 1883 | const owner_decl = scope.decl().?; |
| 1280 | 1884 | ||
| 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 | }; |
| 1289 | 1893 | ||
| 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); |
| 1298 | 1902 | ||
| 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); |
| 1307 | 1911 | ||
| 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 | } |
| 1322 | 1940 | ||
| 1323 | /// TODO should not need the cast on the last parameter at the callsites | ||
| 1324 | fn addNewInstArgs( | 1941 | fn 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 | } |
| 1336 | 1953 | ||
| 1954 | fn 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 | |||
| 1973 | pub 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 | |||
| 1988 | pub 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. | ||
| 2001 | pub 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. | ||
| 2007 | pub 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 | |||
| 1337 | fn addNewInst(self: *Module, block: *Scope.Block, src: usize, ty: Type, comptime T: type) !*T { | 2012 | fn 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 | } |
| 1350 | 2025 | ||
| ... | @@ -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 | } |
| 1363 | 2038 | ||
| 1364 | fn 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 | |||
| 1377 | fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | 2039 | fn 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 | } |
| 1390 | 2052 | ||
| 2053 | fn 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 | |||
| 1391 | fn constUndef(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { | 2060 | fn 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 | } |
| 1453 | 2122 | ||
| 1454 | fn analyzeInstConst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!TypedValue { | 2123 | fn 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 | } |
| 1461 | 2130 | ||
| 2131 | fn 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 | |||
| 1462 | fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { | 2138 | fn 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 | |||
| 2183 | fn 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 | |||
| 2202 | fn 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 | |||
| 2238 | fn getNextAnonNameIndex(self: *Module) usize { | ||
| 2239 | return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic); | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | pub 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 | |||
| 2248 | fn 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 | } |
| 1505 | 2255 | ||
| 1506 | fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst { | 2256 | fn 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 | } |
| 1509 | 2259 | ||
| 2260 | fn 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 | |||
| 2275 | fn 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 | |||
| 1510 | fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst { | 2323 | fn 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 | } |
| 1514 | 2327 | ||
| 1515 | fn analyzeInstRef(self: *Module, scope: *Scope, inst: *zir.Inst.Ref) InnerError!*Inst { | 2328 | fn 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 | } |
| 1519 | 2333 | ||
| 1520 | fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst { | 2334 | fn 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 | |||
| 2340 | fn 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 | |||
| 2363 | fn 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}); | ||
| 1526 | 2367 | ||
| 1527 | const decl = try self.resolveCompleteDecl(scope, src_decl); | 2368 | fn 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 | } |
| 1530 | 2371 | ||
| 1531 | fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl { | 2372 | fn 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) orelse | 2375 | 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}); |
| 1537 | 2377 | ||
| 1538 | const decl = try self.resolveCompleteDecl(scope, src_decl); | 2378 | const decl = try self.resolveCompleteZirDecl(scope, src_decl.decl); |
| 1539 | 2379 | ||
| 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 | } |
| 1548 | 2388 | ||
| 2389 | fn 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 | |||
| 1549 | fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { | 2395 | fn 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 | } |
| 1560 | 2422 | ||
| 2423 | fn 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 | |||
| 1561 | fn analyzeInstCall(self: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { | 2429 | fn 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 |
| 1605 | 2473 | ||
| 1606 | // TODO handle function calls of generic functions | 2474 | // TODO handle function calls of generic functions |
| 1607 | 2475 | ||
| 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); |
| 1611 | 2479 | ||
| 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 | } |
| 1617 | 2485 | ||
| 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 |
| 1624 | 2492 | ||
| 1625 | fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { | 2493 | fn 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 | } |
| 1640 | 2520 | ||
| 2521 | fn analyzeInstIntType(self: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst { | ||
| 2522 | return self.fail(scope, inttype.base.src, "TODO implement inttype", .{}); | ||
| 2523 | } | ||
| 2524 | |||
| 1641 | fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { | 2525 | fn 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); |
| 1643 | 2527 | ||
| 1644 | if (return_type.zigTypeTag() == .NoReturn and | 2528 | // Hot path for some common function types. |
| 1645 | fntype.positionals.param_types.len == 0 and | 2529 | 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 | } | ||
| 1650 | 2533 | ||
| 1651 | if (return_type.zigTypeTag() == .NoReturn and | 2534 | if (return_type.zigTypeTag() == .Void and fntype.kw_args.cc == .Unspecified) { |
| 1652 | fntype.positionals.param_types.len == 0 and | 2535 | 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 | } |
| 1657 | 2546 | ||
| 1658 | if (return_type.zigTypeTag() == .Void and | 2547 | const arena = scope.arena(); |
| 1659 | fntype.positionals.param_types.len == 0 and | 2548 | 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 | } |
| 1664 | 2552 | ||
| 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 | } |
| 1667 | 2561 | ||
| 1668 | fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { | 2562 | fn 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-address | 2577 | // 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 | } |
| 1688 | 2582 | ||
| 1689 | fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst { | 2583 | fn 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 | } |
| 1790 | 2684 | ||
| 2685 | fn 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 | |||
| 1791 | fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!*Inst { | 2689 | fn 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); |
| 1794 | 2695 | ||
| 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 without | 2705 | // 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]; |
| 1811 | 2718 | ||
| 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 | } | ||
| 1833 | 2735 | ||
| 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 | } |
| 1836 | 2744 | ||
| 1837 | fn analyzeInstDeref(self: *Module, scope: *Scope, deref: *zir.Inst.Deref) InnerError!*Inst { | 2745 | fn 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 | } |
| 1876 | 2784 | ||
| 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 and | 2830 | } 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 | } |
| 1955 | 2855 | ||
| 2856 | fn 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 | |||
| 1956 | fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst { | 2867 | fn 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); |
| 1977 | 2888 | ||
| 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); |
| 1986 | 2898 | ||
| 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); |
| 1995 | 2908 | ||
| 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 | } |
| 2020 | 2933 | ||
| 2021 | fn analyzeInstRet(self: *Module, scope: *Scope, inst: *zir.Inst.Return) InnerError!*Inst { | 2934 | fn 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 | |||
| 2940 | fn 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 | } |
| 2025 | 2944 | ||
| 2026 | fn analyzeBody(self: *Module, scope: *Scope, body: zir.Module.Body) !void { | 2945 | fn 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 | } |
| 2041 | 2950 | ||
| ... | @@ -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); |
| 2224 | 3133 | ||
| 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 | } |
| 2243 | 3152 | ||
| 3153 | fn 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 | |||
| 2244 | fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | 3178 | fn 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 errors | 3237 | // 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 | } |
| 2304 | 3241 | ||
| 2305 | fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | 3242 | fn 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 | } |
| 2312 | 3249 | ||
| 2313 | fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: var) InnerError { | 3250 | pub 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 | } |
| 2318 | 3255 | ||
| 3256 | pub 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 | |||
| 3268 | pub 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 | |||
| 2319 | fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError { | 3280 | fn 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, |
| 2362 | 3342 | ||
| 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 | } |
| 2369 | 3349 | ||
| 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 | } |
| 2375 | 3355 | ||
| 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 | } |
| 2382 | 3362 | ||
| 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 | |||
| 3369 | fn 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. | ||
| 26 | pub 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const mem = std.mem; | ||
| 3 | const Value = @import("value.zig").Value; | ||
| 4 | const Type = @import("type.zig").Type; | ||
| 5 | const TypedValue = @import("TypedValue.zig"); | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | const zir = @import("zir.zig"); | ||
| 8 | const Module = @import("Module.zig"); | ||
| 9 | const ast = std.zig.ast; | ||
| 10 | const trace = @import("tracy.zig").trace; | ||
| 11 | const Scope = Module.Scope; | ||
| 12 | const InnerError = Module.InnerError; | ||
| 13 | |||
| 14 | /// Turn Zig AST into untyped ZIR istructions. | ||
| 15 | pub 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 | |||
| 41 | pub 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 | |||
| 66 | fn 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 | |||
| 102 | fn 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 | |||
| 109 | fn 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 | |||
| 124 | fn 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 | |||
| 134 | fn 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 | |||
| 153 | fn 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 | |||
| 235 | fn 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 | |||
| 255 | fn 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 | |||
| 321 | fn 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 | |||
| 340 | fn 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 | |||
| 371 | fn 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 | |||
| 404 | fn 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 | |||
| 443 | fn 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 | |||
| 460 | fn 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 | |||
| 466 | fn 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 | |||
| 527 | fn 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"); |
| 10 | const ErrorMsg = Module.ErrorMsg; | 10 | const ErrorMsg = Module.ErrorMsg; |
| 11 | const Target = std.Target; | 11 | const Target = std.Target; |
| 12 | const Allocator = mem.Allocator; | 12 | const Allocator = mem.Allocator; |
| 13 | const trace = @import("tracy.zig").trace; | ||
| 14 | |||
| 15 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. | ||
| 16 | pub const BlockData = struct { | ||
| 17 | relocs: std.ArrayListUnmanaged(Reloc) = .{}, | ||
| 18 | }; | ||
| 19 | |||
| 20 | pub 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 | }; | ||
| 13 | 26 | ||
| 14 | pub const Result = union(enum) { | 27 | pub 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 | }; |
| 21 | 34 | ||
| 22 | pub fn generateSymbol( | 35 | pub 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; |
| 35 | 51 | ||
| 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(); | ||
| 45 | 80 | ||
| 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 | }; | ||
| 53 | 91 | ||
| 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 | } |
| 148 | 186 | ||
| 187 | const InnerError = error{ | ||
| 188 | OutOfMemory, | ||
| 189 | CodegenFail, | ||
| 190 | }; | ||
| 191 | |||
| 192 | const 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 | |||
| 149 | const Function = struct { | 251 | const 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, | ||
| 156 | 261 | ||
| 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 can | 269 | 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 | }; |
| 170 | 296 | ||
| 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 bodies | 404 | .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 | } |
| 187 | 415 | ||
| 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); // int3 | 616 | 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 | } |
| 197 | 622 | ||
| 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 | } | ||
| 205 | 658 | ||
| 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 | } |
| 232 | 687 | ||
| 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); // ret | 694 | 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 | } |
| 242 | 706 | ||
| 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 | } |
| 248 | 748 | ||
| 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 | } |
| 254 | 799 | ||
| 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 | } |
| 260 | 815 | ||
| 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 default | 817 | // 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 | } |
| 268 | 823 | ||
| 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 signed | 868 | // 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 | } |
| 287 | 879 | ||
| 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 function | 881 | 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 of | 922 | /// * 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 | } |
| 406 | 943 | ||
| 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. Since | 982 | // 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 0b0100100B | 1021 | // In this case, the encoding of the REX byte is 0b0100100B |
| 466 | 1022 | 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 three | 1037 | // 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 middle | 1059 | // 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 load | 1087 | // 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/M | 1115 | // 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 register | 1117 | // 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 | } |
| 585 | 1145 | ||
| 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 | } |
| 598 | 1166 | ||
| 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 this | 1220 | .ComptimeInt => unreachable, // semantic analysis prevents this |
| 621 | .ComptimeFloat => unreachable, // semantic analysis prevents this | 1221 | .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 | } |
| 625 | 1225 | ||
| 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | const link = @import("../link.zig"); | ||
| 4 | const Module = @import("../Module.zig"); | ||
| 5 | |||
| 6 | const Inst = @import("../ir.zig").Inst; | ||
| 7 | const Value = @import("../value.zig").Value; | ||
| 8 | const Type = @import("../type.zig").Type; | ||
| 9 | |||
| 10 | const C = link.File.C; | ||
| 11 | const Decl = Module.Decl; | ||
| 12 | const 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. | ||
| 16 | fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { | ||
| 17 | return allocator.dupe(u8, name); | ||
| 18 | } | ||
| 19 | |||
| 20 | fn 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 | |||
| 44 | fn 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 | |||
| 56 | pub 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 | |||
| 64 | fn 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 | |||
| 81 | fn 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 | |||
| 108 | fn 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 | |||
| 126 | fn 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 | |||
| 155 | fn 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 @@ |
| 1 | const Type = @import("../Type.zig"); | ||
| 2 | |||
| 1 | // zig fmt: off | 3 | // zig fmt: off |
| 2 | 4 | ||
| 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 sixteen | 7 | /// 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, and | 10 | /// 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, it | 11 | /// 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-bit | 12 | /// 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 is | 17 | /// 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 | /// | ||
| 18 | pub const Register = enum(u8) { | 19 | pub 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 | }; |
| 68 | 69 | ||
| 69 | // zig fmt: on | 70 | // 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 | } |
| 301 | 301 | ||
| 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 | } |
| 306 | 306 | ||
| 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 | } |
| 318 | 318 | ||
| 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 | } |
| 885 | 885 | ||
| 886 | fn printSection(out: var, label: []const u8, bytes: []const u8) !void { | 886 | fn 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 | } |
| 893 | 893 | ||
| 894 | fn printLabel(out: var, label: []const u8, bytes: []const u8) !void { | 894 | fn 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 | } |
| 905 | 905 | ||
| 906 | fn printRuler(out: var) !void { | 906 | fn 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 | } |
| 914 | 914 | ||
| 915 | fn hexDump(out: var, bytes: []const u8) !void { | 915 | fn 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 | } |
| 961 | 961 | ||
| 962 | fn hexDump16(out: var, offset: usize, bytes: []const u8) !void { | 962 | fn 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 | } |
| 979 | 979 | ||
| 980 | fn printDecValue(out: var, value: u64, width: u8) !void { | 980 | fn 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 | } |
| 985 | 985 | ||
| 986 | fn printHexValue(out: var, value: u64, width: u8) !void { | 986 | fn 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 | } |
| 991 | 991 | ||
| 992 | fn printCharValues(out: var, bytes: []const u8) !void { | 992 | fn 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 parameter | 1020 | // output: must be a function that takes a `self` idiom parameter |
| 1021 | // and a bytes parameter | 1021 | // and a bytes parameter |
| 1022 | // context: must be that self | 1022 | // context: must be that self |
| 1023 | fn makeOutput(comptime output: var, context: var) Output(output, @TypeOf(context)) { | 1023 | fn 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 | } |
| 1028 | 1028 | ||
| 1029 | fn Output(comptime output_func: var, comptime Context: type) type { | 1029 | fn Output(comptime output_func: anytype, comptime Context: type) type { |
| 1030 | return struct { | 1030 | return struct { |
| 1031 | context: Context, | 1031 | context: Context, |
| 1032 | 1032 |
src-self-hosted/ir.zig+121-2| ... | @@ -2,6 +2,8 @@ const std = @import("std"); | ... | @@ -2,6 +2,8 @@ const std = @import("std"); |
| 2 | const Value = @import("value.zig").Value; | 2 | const Value = @import("value.zig").Value; |
| 3 | const Type = @import("type.zig").Type; | 3 | const Type = @import("type.zig").Type; |
| 4 | const Module = @import("Module.zig"); | 4 | const Module = @import("Module.zig"); |
| 5 | const assert = std.debug.assert; | ||
| 6 | const codegen = @import("codegen.zig"); | ||
| 5 | 7 | ||
| 6 | /// These are in-memory, analyzed instructions. See `zir.Inst` for the representation | 8 | /// 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. |
| 11 | pub const Inst = struct { | 13 | pub 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, |
| 16 | 29 | ||
| 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 | }; |
| 31 | 70 | ||
| 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 | } |
| 51 | 90 | ||
| 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 | }; |
| 74 | 129 | ||
| 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 | }; |
| 80 | 154 | ||
| 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 | }; |
| 111 | 208 | ||
| ... | @@ -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 | }; |
| 151 | 256 | ||
| 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 | |||
| 274 | pub 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 = .{}; |
| 43 | 43 | ||
| ... | @@ -115,7 +115,7 @@ pub const LibCInstallation = struct { | ... | @@ -115,7 +115,7 @@ pub const LibCInstallation = struct { |
| 115 | return self; | 115 | return self; |
| 116 | } | 116 | } |
| 117 | 117 | ||
| 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"); |
| 7 | const fs = std.fs; | 7 | const fs = std.fs; |
| 8 | const elf = std.elf; | 8 | const elf = std.elf; |
| 9 | const codegen = @import("codegen.zig"); | 9 | const codegen = @import("codegen.zig"); |
| 10 | const c_codegen = @import("codegen/c.zig"); | ||
| 10 | 11 | ||
| 11 | const default_entry_addr = 0x8000000; | 12 | const default_entry_addr = 0x8000000; |
| 12 | 13 | ||
| ... | @@ -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(); |
| 38 | 40 | ||
| 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 | } |
| 43 | 54 | ||
| 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 | } |
| 77 | 88 | ||
| 89 | fn 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. |
| 83 | pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile { | 106 | pub 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 | } |
| 91 | 114 | ||
| 92 | pub const ElfFile = struct { | 115 | pub 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 | } | ||
| 212 | 120 | ||
| 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 | }; | ||
| 229 | 123 | ||
| 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 | } |
| 245 | 131 | ||
| 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 | } |
| 253 | 138 | ||
| 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 | } |
| 263 | 146 | ||
| 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 | } | ||
| 282 | 154 | ||
| 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 | } | ||
| 292 | 162 | ||
| 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 | } |
| 309 | 178 | ||
| 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 | } |
| 328 | 186 | ||
| 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 | } |
| 336 | 193 | ||
| 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 | } |
| 344 | 201 | ||
| 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 | } |
| 349 | 208 | ||
| 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 | } |
| 357 | 221 | ||
| 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.?]; | ||
| 434 | 226 | ||
| 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.?]; | ||
| 452 | 251 | ||
| 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; |
| 474 | 267 | }; | |
| 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 | } | ||
| 523 | 455 | ||
| 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 | } | ||
| 527 | 463 | ||
| 528 | // Unfortunately these have to be buffered and done at the end because ELF does not allow | 464 | 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 | } | ||
| 531 | 473 | ||
| 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 | } | ||
| 543 | 492 | ||
| 544 | if (needed_size > allocated_size) { | 493 | if (self.phdr_table_offset) |off| { |
| 545 | self.phdr_table_offset = null; // free the space | 494 | 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 | } |
| 548 | 502 | ||
| 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 | } | ||
| 553 | 519 | ||
| 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 | } | ||
| 565 | 538 | ||
| 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 | } |
| 577 | 546 | ||
| 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 | } | ||
| 583 | 554 | ||
| 584 | if (needed_size > allocated_size) { | 555 | fn getString(self: *Elf, str_off: u32) []const u8 { |
| 585 | shstrtab_sect.sh_size = 0; // free the space | 556 | 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 }); | ||
| 590 | 559 | ||
| 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); |
| 610 | 711 | self.shdr_table_dirty = true; | |
| 611 | if (needed_size > allocated_size) { | 712 | } |
| 612 | self.shdr_table_offset = null; // free the space | 713 | 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 | } | ||
| 615 | 733 | ||
| 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); | ||
| 620 | 737 | ||
| 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); | ||
| 632 | 800 | ||
| 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; | ||
| 651 | 820 | ||
| 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 | } | ||
| 660 | 825 | ||
| 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); | ||
| 663 | 830 | ||
| 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 | } | ||
| 667 | 862 | ||
| 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 | } | ||
| 673 | 871 | ||
| 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; | ||
| 680 | 874 | ||
| 681 | hdr_buf[index] = 1; // ELF version | 875 | var index: usize = 0; |
| 682 | index += 1; | 876 | hdr_buf[0..4].* = "\x7fELF".*; |
| 877 | index += 4; | ||
| 683 | 878 | ||
| 684 | // OS ABI, often set to 0 regardless of target platform | 879 | hdr_buf[index] = switch (self.ptr_width) { |
| 685 | // ABI Version, possibly used by glibc but not by static executables | 880 | .p32 => elf.ELFCLASS32, |
| 686 | // padding | 881 | .p64 => elf.ELFCLASS64, |
| 687 | mem.set(u8, hdr_buf[index..][0..9], 0); | 882 | }; |
| 688 | index += 9; | 883 | index += 1; |
| 689 | 884 | ||
| 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; | ||
| 691 | 891 | ||
| 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; | ||
| 702 | 894 | ||
| 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; | ||
| 706 | 900 | ||
| 707 | // ELF Version, again | 901 | assert(index == 16); |
| 708 | mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian); | ||
| 709 | index += 4; | ||
| 710 | 902 | ||
| 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; | ||
| 712 | 913 | ||
| 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; | ||
| 717 | 917 | ||
| 718 | // e_phoff | 918 | // 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; |
| 721 | 921 | ||
| 722 | // e_shoff | 922 | 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 | } | ||
| 740 | 923 | ||
| 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; | ||
| 744 | 928 | ||
| 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; | ||
| 751 | 932 | ||
| 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; | ||
| 758 | 941 | ||
| 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; |
| 762 | 945 | ||
| 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; | ||
| 769 | 951 | ||
| 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; |
| 773 | 955 | ||
| 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; | ||
| 776 | 962 | ||
| 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; | ||
| 778 | 969 | ||
| 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; | ||
| 781 | 973 | ||
| 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 | } | ||
| 797 | 980 | ||
| 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 here | 982 | mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian); |
| 800 | self.last_text_block = text_block.prev; | 983 | index += 2; |
| 801 | } | ||
| 802 | 984 | ||
| 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; |
| 805 | 987 | ||
| 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 | } | ||
| 814 | 989 | ||
| 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 | } | ||
| 821 | 992 | ||
| 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 enough | 994 | var already_have_free_list_node = false; |
| 824 | // capacity, insert a free list node for it. | 995 | { |
| 825 | } | 996 | var i: usize = 0; |
| 826 | 997 | 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 the | 1004 | } |
| 875 | // remaining question is whether there is still yet enough capacity left | 1005 | 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 | }; | ||
| 898 | 1008 | ||
| 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; | ||
| 916 | 1013 | ||
| 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; | ||
| 920 | 1016 | ||
| 921 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty | 1017 | if (!already_have_free_list_node and prev.freeListEligible(self.*)) { |
| 922 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty | 1018 | // 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 | } | ||
| 924 | 1025 | ||
| 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 before | 1027 | 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 | } |
| 934 | 1032 | ||
| 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 | } | ||
| 948 | 1037 | ||
| 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]; |
| 951 | 1040 | 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 | } |
| 966 | 1045 | ||
| 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 | }; | ||
| 974 | 1109 | ||
| 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; | ||
| 976 | 1127 | ||
| 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 | } | ||
| 987 | 1131 | ||
| 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); | ||
| 993 | 1135 | ||
| 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 | } | ||
| 995 | 1145 | ||
| 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 | } | ||
| 999 | 1159 | ||
| 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 | }; | ||
| 1014 | 1162 | ||
| 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); | ||
| 1016 | 1168 | ||
| 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 | } | ||
| 1021 | 1177 | ||
| 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 or | 1182 | _ = 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 | } |
| 1068 | 1198 | ||
| 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); | ||
| 1072 | 1204 | ||
| 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 | } | ||
| 1077 | 1206 | ||
| 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; | ||
| 1147 | 1275 | ||
| 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 | } | ||
| 1152 | 1279 | ||
| 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 | } | ||
| 1158 | 1283 | ||
| 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 | } | ||
| 1180 | 1288 | ||
| 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 | }; | ||
| 1203 | 1358 | ||
| 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; | ||
| 1226 | 1363 | ||
| 1227 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty | 1364 | pub fn deleteExport(self: *Elf, exp: Export) void { |
| 1228 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty | 1365 | 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 | } | ||
| 1229 | 1369 | ||
| 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 | } | ||
| 1247 | 1414 | ||
| 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 relocated | 1417 | 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(); |
| 1303 | 1486 | 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 | }; |
| 1355 | 1566 | ||
| 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. |
| 1358 | pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !ElfFile { | 1569 | pub 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 enum | 1577 | .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 | } |
| 1371 | 1583 | ||
| 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 | } |
| 1414 | 1626 | ||
| 1415 | /// Returns error.IncrFailed if incremental update could not be performed. | 1627 | /// Returns error.IncrFailed if incremental update could not be performed. |
| 1416 | fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !ElfFile { | 1628 | fn 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 enum | 1635 | .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 | } |
| 1447 | 1660 | ||
| 1448 | /// Saturating multiplication | 1661 | /// Saturating multiplication |
| 1449 | fn satMul(a: var, b: var) @TypeOf(a, b) { | 1662 | fn 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const ir = @import("ir.zig"); | ||
| 3 | const trace = @import("tracy.zig").trace; | ||
| 4 | |||
| 5 | /// Perform Liveness Analysis over the `Body`. Each `Inst` will have its `deaths` field populated. | ||
| 6 | pub 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 | |||
| 22 | fn 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 | |||
| 32 | fn 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 | |||
| 49 | fn 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 | ; |
| 40 | 40 | ||
| 41 | pub 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 | |||
| 41 | pub fn main() !void { | 67 | pub fn main() !void { |
| 42 | // TODO general purpose allocator in the zig std lib | 68 | // 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); |
| 49 | 75 | ||
| 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 | } |
| 54 | 80 | ||
| ... | @@ -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 code | 115 | \\ .zig Zig source code |
| 90 | \\ .zir Zig Intermediate Representation code | 116 | \\ .zir Zig Intermediate Representation code |
| 91 | \\ (planned) .o ELF object file | 117 | \\ (planned) .o ELF object file |
| 92 | \\ (planned) .o MACH-O (macOS) object file | 118 | \\ (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; | ||
| 172 | 199 | ||
| 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") or | 343 | } else if (mem.endsWith(u8, arg, ".o") or |
| 311 | mem.endsWith(u8, arg, ".obj") or | 344 | mem.endsWith(u8, arg, ".obj") or |
| 312 | mem.endsWith(u8, arg, ".a") or | 345 | 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") or | 350 | } 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") or | 355 | } else if (mem.endsWith(u8, arg, ".so") or |
| 323 | mem.endsWith(u8, arg, ".dylib") or | 356 | 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 | }; |
| 351 | 384 | ||
| 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 | } |
| 356 | 389 | ||
| ... | @@ -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 | }; |
| 390 | 423 | ||
| 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 implementing | 426 | // 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 | } |
| 399 | 431 | ||
| 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 | }; |
| 404 | 436 | ||
| 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 | }; |
| 427 | 449 | ||
| ... | @@ -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(); |
| 455 | 478 | ||
| ... | @@ -487,20 +510,24 @@ fn buildOutputType( | ... | @@ -487,20 +510,24 @@ fn buildOutputType( |
| 487 | } | 510 | } |
| 488 | 511 | ||
| 489 | fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void { | 512 | fn 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(); | ||
| 491 | 516 | ||
| 492 | var errors = try module.getAllErrorsAlloc(); | 517 | var errors = try module.getAllErrorsAlloc(); |
| 493 | defer errors.deinit(module.allocator); | 518 | defer errors.deinit(module.gpa); |
| 494 | 519 | ||
| 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 | } |
| 505 | 532 | ||
| 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), | ||
| 549 | 577 | ||
| 550 | const SeenMap = std.BufSet; | 578 | const SeenMap = std.AutoHashMap(fs.File.INode, void); |
| 551 | }; | 579 | }; |
| 552 | 580 | ||
| 553 | pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { | 581 | 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,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 { |
| 599 | 627 | ||
| 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 | } |
| 605 | 633 | ||
| ... | @@ -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); |
| 610 | 638 | ||
| 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 | } |
| 633 | 661 | ||
| 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 | } |
| 638 | 666 | ||
| ... | @@ -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(); | ||
| 645 | 676 | ||
| 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; |
| 674 | 713 | ||
| 675 | fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void { | 714 | fn 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 them | 715 | 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 | } |
| 709 | 724 | ||
| 710 | const tree = std.zig.parse(fmt.gpa, source_code) catch |err| { | 725 | fn 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 | |||
| 758 | fn 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(); |
| 716 | 788 | ||
| 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); |
| 734 | 806 | 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 | } |
| 742 | 819 |
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 stream | 64 | /// 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 | } |
| 655 | 655 | ||
| 656 | fn 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 warning | 656 | // ABI warning |
| 674 | const Stage2Target = extern struct { | 657 | const 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 { |
| 887 | 870 | ||
| 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; |
| 5 | const zir = @import("zir.zig"); | 5 | const zir = @import("zir.zig"); |
| 6 | const Package = @import("Package.zig"); | 6 | const Package = @import("Package.zig"); |
| 7 | 7 | ||
| 8 | const cheader = @embedFile("cbe.h"); | ||
| 9 | |||
| 8 | test "self-hosted" { | 10 | test "self-hosted" { |
| 9 | var ctx: TestContext = undefined; | 11 | var ctx = TestContext.init(); |
| 10 | try ctx.init(); | ||
| 11 | defer ctx.deinit(); | 12 | defer ctx.deinit(); |
| 12 | 13 | ||
| 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 | } |
| 17 | 18 | ||
| 19 | const ErrorMsg = struct { | ||
| 20 | msg: []const u8, | ||
| 21 | line: u32, | ||
| 22 | column: u32, | ||
| 23 | }; | ||
| 24 | |||
| 18 | pub const TestContext = struct { | 25 | pub 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 | }; | ||
| 21 | 52 | ||
| 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 | }; |
| 27 | 57 | ||
| 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, | ||
| 32 | 74 | ||
| 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 | } |
| 49 | 83 | ||
| 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 | }; |
| 57 | 140 | ||
| 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 | } |
| 70 | 161 | ||
| 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 | } |
| 89 | 182 | ||
| 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 | } |
| 103 | 322 | ||
| 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 | } |
| 110 | 385 | ||
| 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 | } |
| 116 | 398 | ||
| 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(); |
| 122 | 403 | ||
| 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, .{}); |
| 124 | 405 | ||
| 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(); | ||
| 147 | 408 | ||
| 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(); | ||
| 170 | 412 | ||
| 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; | ||
| 172 | 417 | ||
| 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 | } |
| 211 | 423 | ||
| 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(); |
| 221 | 427 | ||
| 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(); |
| 229 | 431 | ||
| 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(); |
| 239 | 450 | ||
| 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(); |
| 244 | 455 | ||
| 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(); | ||
| 246 | 460 | ||
| 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(); |
| 251 | 466 | ||
| 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 }); |
| 259 | 474 | } | |
| 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) { |
| 266 | 481 | .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 | |||
| 305 | fn 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 @@ | ||
| 1 | pub const std = @import("std"); | ||
| 2 | |||
| 3 | pub const enable = if (std.builtin.is_test) false else @import("build_options").enable_tracy; | ||
| 4 | |||
| 5 | extern 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 | |||
| 11 | extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void; | ||
| 12 | |||
| 13 | pub 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 | |||
| 21 | pub 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 | |||
| 30 | pub const Ctx = if (enable) ___tracy_c_zone_context else struct { | ||
| 31 | pub fn end(self: Ctx) void {} | ||
| 32 | }; | ||
| 33 | |||
| 34 | pub 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}; |
| 20 | const TypeError = Error || error{UnsupportedType}; | 20 | const TypeError = Error || error{UnsupportedType}; |
| 21 | const TransError = TypeError || error{UnsupportedTranslation}; | 21 | const TransError = TypeError || error{UnsupportedTranslation}; |
| 22 | 22 | ||
| 23 | const DeclTable = std.HashMap(usize, []const u8, addrHash, addrEql); | 23 | const DeclTable = std.HashMap(usize, []const u8, addrHash, addrEql, false); |
| 24 | 24 | ||
| 25 | fn addrHash(x: usize) u32 { | 25 | fn 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}); |
| 595 | 591 | ||
| 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 | } |
| 628 | 634 | ||
| ... | @@ -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 | }; |
| 727 | 733 | ||
| 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 | } |
| 747 | 750 | ||
| ... | @@ -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 | } |
| 777 | 780 | ||
| 778 | fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_level_visit: bool) Error!?*ast.Node { | 781 | fn 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 twice | 783 | return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice |
| 781 | const rp = makeRestorePoint(c); | 784 | const rp = makeRestorePoint(c); |
| 782 | 785 | ||
| 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 |
| 795 | 798 | ||
| 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 | } |
| 801 | 804 | ||
| 802 | fn transCreateNodeTypedef(rp: RestorePoint, typedef_decl: *const ZigClangTypedefNameDecl, toplevel: bool, checked_name: []const u8) Error!?*ast.Node.VarDecl { | 805 | fn 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, |
| 805 | 808 | 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, ";"); | ||
| 815 | 825 | ||
| 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 | } |
| 819 | 837 | ||
| 820 | fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node { | 838 | fn 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 twice | 840 | 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); |
| 824 | 842 | ||
| 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); |
| 849 | 867 | ||
| 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); | ||
| 851 | 871 | ||
| 852 | node.eq_token = try appendToken(c, .Equal, "="); | 872 | const eq_token = try appendToken(c, .Equal, "="); |
| 853 | 873 | ||
| 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 | }); | ||
| 963 | 992 | ||
| 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!?* |
| 969 | 998 | ||
| 970 | fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node { | 999 | fn 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 twice | 1001 | 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); |
| 975 | 1004 | ||
| ... | @@ -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 |
| 982 | 1011 | ||
| 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, "="); | ||
| 987 | 1014 | ||
| 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 |
| 1063 | 1095 | ||
| 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 exhaustive | 1128 | // 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 | } else | 1152 | } else |
| 1110 | try transCreateNodeOpaqueType(c); | 1153 | try transCreateNodeOpaqueType(c); |
| 1111 | 1154 | ||
| 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 | }); | ||
| 1113 | 1165 | ||
| 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 | } |
| 1119 | 1171 | ||
| 1120 | fn createAlias(c: *Context, alias: var) !void { | 1172 | fn 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 | } |
| 1127 | 1191 | ||
| ... | @@ -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); | ||
| 1465 | 1533 | ||
| 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); |
| 1469 | 1537 | ||
| 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 | else | 1541 | 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)) orelse | 1572 | 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 | } |
| 1563 | 1640 | ||
| 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); |
| 1566 | 1643 | ||
| 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); |
| 1617 | 1694 | ||
| 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 properly | 1699 | // 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 | } |
| 1660 | 1737 | ||
| 1661 | fn isBoolRes(res: *ast.Node) bool { | 1738 | fn 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, | ||
| 1673 | 1751 | ||
| 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 | } |
| 1687 | 1757 | ||
| 1688 | fn finishBoolExpr( | 1758 | fn 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); |
| 2136 | 2206 | ||
| ... | @@ -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 | } |
| 2182 | 2247 | ||
| ... | @@ -2244,11 +2309,11 @@ fn transInitListExprArray( | ... | @@ -2244,11 +2309,11 @@ fn transInitListExprArray( |
| 2244 | &filler_init_node.base | 2309 | &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 | } |
| 2260 | 2325 | ||
| 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); |
| 2657 | 2722 | ||
| 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 | else | 3101 | 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"); |
| 3116 | 3181 | ||
| 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); |
| 3124 | 3198 | ||
| 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"); |
| 3182 | 3256 | ||
| 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); |
| 3190 | 3273 | ||
| 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, ";"); |
| 3194 | 3277 | ||
| 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); |
| 3201 | 3293 | ||
| 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 case | 3372 | // common case |
| 3274 | // c: lhs += rhs | 3373 | // c: lhs += rhs |
| 3275 | // zig: lhs += rhs | 3374 | // 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( |
| 3295 | 3394 | ||
| 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 | else | 3399 | else |
| 3301 | try transExpr(rp, scope, rhs, .used, .r_value); | 3400 | try transExpr(rp, scope, rhs, .used, .r_value); |
| 3302 | 3401 | ||
| 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"); |
| 3326 | 3428 | ||
| 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); |
| 3334 | 3445 | ||
| 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( |
| 3338 | 3449 | ||
| 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); |
| 3360 | 3471 | ||
| 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, ";"); |
| 3373 | 3487 | ||
| 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 | } |
| 3378 | 3492 | ||
| ... | @@ -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(); |
| 3491 | 3605 | ||
| 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); |
| 3498 | 3621 | ||
| 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(); |
| 3929 | 4052 | ||
| 3930 | const tmp = try block_scope.makeMangledName(rp.c, "tmp"); | 4053 | const tmp = try block_scope.makeMangledName(rp.c, "tmp"); |
| 3931 | 4054 | 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); |
| 3944 | 4075 | ||
| 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, ";"); |
| 3949 | 4080 | ||
| 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); |
| 3952 | 4083 | ||
| 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 | } |
| 3962 | 4093 | ||
| 3963 | fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node { | 4094 | fn 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 | } |
| 3973 | 4104 | ||
| 3974 | fn transCreateNodePrefixOp( | 4105 | fn 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 afterward | 4115 | .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 | else | 4132 | 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 afterward | 4211 | .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 | } |
| 4176 | 4305 | ||
| 4177 | fn transCreateNodeInt(c: *Context, int: var) !*ast.Node { | 4306 | fn 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 | } |
| 4185 | 4314 | ||
| 4186 | fn transCreateNodeFloat(c: *Context, int: var) !*ast.Node { | 4315 | fn 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 |
| 4231 | 4360 | ||
| 4232 | _ = try appendToken(c, .RParen, ")"); | 4361 | _ = try appendToken(c, .RParen, ")"); |
| 4233 | 4362 | ||
| 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, "{"); |
| 4253 | 4364 | ||
| 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").?); |
| 4256 | 4367 | ||
| 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 | } |
| 4282 | 4404 | ||
| ... | @@ -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 | } |
| 4357 | 4479 | ||
| 4358 | fn 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 | |||
| 4383 | fn transCreateNodeWhile(c: *Context) !*ast.Node.While { | 4480 | fn 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, ")"); |
| 4460 | 4557 | ||
| 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 | }; |
| 4468 | 4565 | ||
| ... | @@ -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 |
| 4586 | 4683 | ||
| 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 | } |
| 4796 | 4880 | ||
| 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 | } |
| 4801 | 4885 | 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 | } | ||
| 4810 | 4887 | ||
| 4811 | const rparen_tok = try appendToken(rp.c, .RParen, ")"); | 4888 | const rparen_tok = try appendToken(rp.c, .RParen, ")"); |
| 4812 | 4889 | ||
| ... | @@ -4872,44 +4949,53 @@ fn finishTransFnProto( | ... | @@ -4872,44 +4949,53 @@ fn finishTransFnProto( |
| 4872 | } | 4949 | } |
| 4873 | }; | 4950 | }; |
| 4874 | 4951 | ||
| 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 model | 4966 | .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 | } |
| 4894 | 4980 | ||
| 4895 | fn revertAndWarn( | 4981 | fn 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 | } |
| 4906 | 4992 | ||
| 4907 | fn emitWarning(c: *Context, loc: ZigClangSourceLocation, comptime format: []const u8, args: var) !void { | 4993 | fn 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 | } |
| 4911 | 4997 | ||
| 4912 | pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comptime format: []const u8, args: var) !void { | 4998 | pub 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; |
| 4937 | 5023 | ||
| 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 | } |
| 4957 | 5035 | ||
| ... | @@ -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 | } |
| 4962 | 5040 | ||
| 4963 | fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: var) !ast.TokenIndex { | 5041 | fn 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); |
| 4965 | 5043 | ||
| 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 { |
| 5144 | fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void { | 5222 | fn 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; |
| 5146 | 5224 | ||
| 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, "="); | ||
| 5149 | 5229 | ||
| 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 | ); |
| 5160 | 5240 | ||
| 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 | } |
| 5164 | 5253 | ||
| ... | @@ -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, ":"); |
| 5204 | 5293 | ||
| 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 | }; |
| 5210 | 5298 | ||
| 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 | }; |
| 5218 | 5306 | ||
| 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, |
| 5236 | 5324 | ||
| 5237 | const type_of = try c.createBuiltinCall("@TypeOf", 1); | 5325 | const type_of = try c.createBuiltinCall("@TypeOf", 1); |
| 5238 | 5326 | ||
| 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, |
| 5279 | 5349 | ||
| 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 | } |
| 5285 | 5366 | ||
| ... | @@ -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 result | 5401 | // 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, |
| 5668 | 5749 | ||
| 5669 | const lparen = try appendToken(c, .LParen, "("); | 5750 | const lparen = try appendToken(c, .LParen, "("); |
| 5670 | 5751 | ||
| 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 | //else | 5755 | 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, ")"); | ||
| 5738 | 5759 | ||
| 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; | ||
| 5821 | 5764 | ||
| 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 | } |
| 5843 | 5786 | ||
| 5787 | fn 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 | |||
| 5844 | fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node { | 5838 | fn 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; |
| 5847 | 5841 | ||
| 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 { |
| 5862 | 5856 | ||
| 5863 | fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node { | 5857 | fn 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; |
| 5866 | 5860 | ||
| 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 { |
| 5875 | 5869 | ||
| 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, |
| 6119 | 6168 | ||
| 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 | } |
| 6161 | 6210 | ||
| 6162 | fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { | 6211 | fn 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 | } |
| 6189 | 6257 | ||
| 6190 | fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { | 6258 | fn 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 | } |
| 6216 | 6282 | ||
| 6217 | fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto { | 6283 | fn 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 | } |
| 6230 | 6294 | ||
| 6231 | fn addMacros(c: *Context) !void { | 6295 | fn 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 that | 6298 | // 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 pointer | 6299 | // 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, |
| 55 | 61 | ||
| 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, | ||
| 59 | 67 | ||
| 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 | } |
| 165 | 283 | ||
| 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 { |
| 206 | 337 | ||
| 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 | }, | ||
| 212 | 354 | ||
| 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 | } |
| 328 | 485 | ||
| 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 pointer | 496 | .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 pointer | 498 | .fn_naked_noreturn_no_args, // represents machine code; not a pointer |
| 337 | .fn_ccc_void_no_args, // represents machine code; not a pointer | 499 | .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, |
| 340 | 503 | ||
| 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 | } |
| 389 | 556 | ||
| 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 | } |
| 658 | 942 | ||
| 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, |
| 693 | 987 | ||
| 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 | } |
| 704 | 1001 | ||
| 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 | } |
| 749 | 1108 | ||
| 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, | ||
| 782 | 1196 | ||
| 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 | }, | ||
| 828 | 1253 | ||
| 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, | ||
| 873 | 1365 | ||
| 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, | ||
| 918 | 1418 | ||
| 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, | ||
| 963 | 1471 | ||
| 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, | ||
| 1194 | 1740 | ||
| 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 { |
| 1229 | 1775 | ||
| 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 | }; |
| 1234 | 1788 |
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 | }; |
| 80 | 87 | ||
| 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 | } |
| 85 | 92 | ||
| ... | @@ -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 | } |
| 109 | 116 | ||
| 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 { |
| 203 | 326 | ||
| 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 { |
| 304 | 439 | ||
| 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(), |
| 308 | 444 | ||
| 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 { |
| 363 | 506 | ||
| 364 | .zero, | 507 | .zero, |
| 365 | .the_one_possible_value, // an integer with one possible value is always zero | 508 | .the_one_possible_value, // an integer with one possible value is always zero |
| 509 | .bool_false, | ||
| 366 | => return 0, | 510 | => return 0, |
| 367 | 511 | ||
| 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 { |
| 423 | 574 | ||
| 424 | .the_one_possible_value, // an integer with one possible value is always zero | 575 | .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, |
| 427 | 579 | ||
| 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 zero | 649 | .the_one_possible_value, // an integer with one possible value is always zero |
| 650 | .bool_false, | ||
| 491 | => return true, | 651 | => return true, |
| 492 | 652 | ||
| 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 { |
| 638 | 819 | ||
| 639 | .zero, | 820 | .zero, |
| 640 | .the_one_possible_value, // an integer with one possible value is always zero | 821 | .the_one_possible_value, // an integer with one possible value is always zero |
| 822 | .bool_false, | ||
| 641 | => return .eq, | 823 | => return .eq, |
| 642 | 824 | ||
| 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 | }; |
| 931 | 1136 | ||
| 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"); |
| 12 | const ir = @import("ir.zig"); | 12 | const ir = @import("ir.zig"); |
| 13 | const IrModule = @import("Module.zig"); | 13 | const IrModule = @import("Module.zig"); |
| 14 | 14 | ||
| 15 | /// This struct is relevent only for the ZIR Module text format. It is not used for | ||
| 16 | /// semantic analysis of Zig source code. | ||
| 17 | pub 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` for | 26 | /// 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. |
| 17 | pub const Inst = struct { | 28 | pub 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. |
| 22 | 33 | 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{}, | ||
| 25 | 34 | ||
| 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 | }; |
| 59 | 134 | ||
| 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 | } |
| 98 | 183 | ||
| 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 | }; |
| 106 | 220 | ||
| 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, |
| 123 | 247 | ||
| 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 | }; |
| 139 | 273 | ||
| 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 | }; |
| 149 | 293 | ||
| 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 | }; |
| 169 | 333 | ||
| 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, |
| 173 | 338 | ||
| ... | @@ -200,6 +365,7 @@ pub const Inst = struct { | ... | @@ -200,6 +365,7 @@ pub const Inst = struct { |
| 200 | 365 | ||
| 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, |
| 204 | 370 | ||
| 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, |
| 240 | 406 | ||
| 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 | }; |
| 255 | 431 | ||
| 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, |
| 259 | 435 | ||
| 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 | }; |
| 266 | 455 | ||
| 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, |
| 270 | 459 | ||
| 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 {}, |
| 285 | 475 | ||
| 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 { |
| 315 | 513 | ||
| 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 | }; |
| 350 | 556 | ||
| 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 | }; |
| 407 | 600 | ||
| 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 | }; |
| 457 | 663 | ||
| 458 | pub const Module = struct { | 664 | pub 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, |
| 462 | 668 | ||
| ... | @@ -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 | } |
| 477 | 683 | ||
| 478 | const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize }); | 684 | const DeclAndIndex = struct { |
| 685 | decl: *Decl, | ||
| 686 | index: usize, | ||
| 687 | }; | ||
| 479 | 688 | ||
| 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 { |
| 489 | 713 | ||
| 490 | /// The allocator is used for temporary storage, but this function always returns | 714 | /// 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 % indexes | 717 | 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(); | ||
| 496 | 727 | ||
| 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); | ||
| 498 | 730 | ||
| 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 }); |
| 501 | 733 | ||
| 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 | } |
| 508 | 740 | ||
| 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 | |||
| 749 | const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize, name: []const u8 }); | ||
| 750 | |||
| 751 | const 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, | ||
| 515 | 757 | ||
| 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 bug | 764 | 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 | } |
| 553 | 771 | ||
| 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 | } |
| 572 | 789 | ||
| 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 | } |
| 593 | 810 | ||
| 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 | } |
| 625 | 856 | ||
| 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 | }; |
| 641 | 875 | ||
| 642 | pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module { | 876 | pub 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(); |
| 645 | 879 | ||
| 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(); |
| 656 | 892 | ||
| 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), | ||
| 680 | 917 | ||
| 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 | }; |
| 685 | 922 | ||
| 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(); | ||
| 693 | 932 | ||
| 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 | } |
| 850 | 1089 | ||
| 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 | } |
| 859 | 1098 | ||
| 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 | }; |
| 886 | 1124 | ||
| 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, ")"); |
| 931 | 1173 | ||
| 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 }); |
| 934 | 1181 | ||
| 935 | return &inst_specific.base; | 1182 | return decl; |
| 936 | } | 1183 | } |
| 937 | 1184 | ||
| 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_map | 1251 | bc.name_map |
| 995 | else | 1252 | else |
| 996 | return self.fail("referencing a % instruction in global scope", .{}) | 1253 | return self.fail("referencing a % instruction in global scope", .{}) |
| 997 | else | 1254 | 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 | } |
| 1033 | 1284 | ||
| 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), | ||
| 1072 | 1328 | ||
| 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 introduced | 1330 | // 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); |
| 1081 | 1337 | ||
| 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); |
| 1093 | 1348 | ||
| 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 | } |
| 1121 | 1409 | ||
| 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 | } |
| 1138 | 1433 | ||
| ... | @@ -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 | } |
| 1152 | 1446 | ||
| 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 | } |
| 1170 | 1462 | ||
| 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 | } |
| 1189 | 1477 | ||
| 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 | }; |
| 1231 | 1518 | 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 | } |
| 1285 | 1569 | ||
| 1286 | const fn_type = try self.emitType(src, module_fn.fn_type); | 1570 | const fn_type = try self.emitType(src, typed_value.ty); |
| 1287 | 1571 | ||
| 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 literal | 1590 | // 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 { |
| 1351 | 1630 | ||
| 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); |
| 1364 | 1765 | ||
| 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 bodies | 1800 | .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 { |
| 1389 | 1804 | ||
| 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 | } |
| 1394 | 1809 | ||
| 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 | } |
| 1399 | 1814 | ||
| 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 | } |
| 1404 | 1819 | ||
| 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 | else | 1833 | 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 | } |
| 1544 | 1952 | ||
| 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 | } |
| 1580 | 1996 | ||
| 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 | } |
| 1615 | 2049 | ||
| 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 | } |
| 1636 | 2068 | ||
| 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 | } | ||
| 1651 | 2083 | ||
| 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); |
| 1665 | 2092 | 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 | }; |
| 697 | 697 | ||
| 698 | enum FnInline { | 698 | enum 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 declaration | 710 | // 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 { |
| 734 | struct AstNodeParamDecl { | 734 | struct 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 | }; |
| 1831 | 1832 | ||
| 1832 | struct BuiltinFnEntry { | 1833 | struct 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 | }; |
| 2758 | 2761 | ||
| 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 | }; |
| 3255 | 3258 | ||
| 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. | ||
| 3262 | struct 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 | |||
| 3256 | struct IrInstGenCall { | 3273 | struct IrInstGenCall { |
| 3257 | IrInstGen base; | 3274 | IrInstGen base; |
| 3258 | 3275 | ||
| ... | @@ -3761,6 +3778,10 @@ struct IrInstGenWasmMemoryGrow { | ... | @@ -3761,6 +3778,10 @@ struct IrInstGenWasmMemoryGrow { |
| 3761 | IrInstGen *delta; | 3778 | IrInstGen *delta; |
| 3762 | }; | 3779 | }; |
| 3763 | 3780 | ||
| 3781 | struct IrInstSrcSrc { | ||
| 3782 | IrInstSrc base; | ||
| 3783 | }; | ||
| 3784 | |||
| 3764 | struct IrInstSrcSlice { | 3785 | struct IrInstSrcSlice { |
| 3765 | IrInstSrc base; | 3786 | IrInstSrc base; |
| 3766 | 3787 |
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 |
| 1230 | Error type_val_resolve_is_opaque_type(CodeGen *g, ZigValue *type_val, bool *is_opaque_type) { | 1230 | Error 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"); |
| 1521 | 1521 | ||
| 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 | } |
| 1944 | 1944 | ||
| 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 | } |
| 3825 | 3825 | ||
| 3826 | ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry) { | 3826 | ZigType *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 | } |
| 3979 | 3982 | ||
| ... | @@ -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 | } |
| 4016 | 4023 | ||
| 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) { |
| 5864 | 5871 | ||
| 5865 | ReqCompTime type_requires_comptime(CodeGen *g, ZigType *ty) { | 5872 | ReqCompTime 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 | } |
| 6014 | 6021 | ||
| 6022 | void 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 | |||
| 6029 | ZigValue *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 | |||
| 6015 | void init_const_float(ZigValue *const_val, ZigType *type, double value) { | 6035 | void 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 |
| 77 | ZigType *get_src_ptr_type(ZigType *type); | 77 | ZigType *get_src_ptr_type(ZigType *type); |
| 78 | uint32_t get_ptr_align(CodeGen *g, ZigType *type); | 78 | uint32_t get_ptr_align(CodeGen *g, ZigType *type); |
| 79 | bool get_ptr_const(CodeGen *g, ZigType *type); | 79 | bool get_ptr_const(CodeGen *g, ZigType *type); |
| 80 | ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry); | 80 | ZigType *validate_var_type(CodeGen *g, AstNodeVariableDeclaration *source_node, ZigType *type_entry); |
| 81 | ZigType *container_ref_type(ZigType *type_entry); | 81 | ZigType *container_ref_type(ZigType *type_entry); |
| 82 | bool type_is_complete(ZigType *type_entry); | 82 | bool type_is_complete(ZigType *type_entry); |
| 83 | bool type_is_resolved(ZigType *type_entry, ResolveStatus status); | 83 | bool 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 |
| 180 | void init_const_null(ZigValue *const_val, ZigType *type); | 180 | void init_const_null(ZigValue *const_val, ZigType *type); |
| 181 | ZigValue *create_const_null(CodeGen *g, ZigType *type); | 181 | ZigValue *create_const_null(CodeGen *g, ZigType *type); |
| 182 | 182 | ||
| 183 | void init_const_fn(ZigValue *const_val, ZigFn *fn); | ||
| 184 | ZigValue *create_const_fn(CodeGen *g, ZigFn *fn); | ||
| 185 | |||
| 183 | ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count); | 186 | ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count); |
| 184 | ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count); | 187 | ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count); |
| 185 | 188 |
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 | } |
| 498 | 498 | ||
| 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 | } |
| 1537 | 1537 | ||
| 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); |
| 1548 | 1550 | ||
| 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); |
| 1551 | 1553 | ||
| 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 |
| 3540 | 3542 | ||
| 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); |
| 3989 | 3991 | ||
| 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) { |
| 5258 | 5260 | ||
| 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 |
| 5583 | 5585 | ||
| 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 | } |
| 8442 | 8454 | ||
| 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 | } |
| 8718 | 8731 | ||
| 8719 | static const char *bool_to_str(bool b) { | 8732 | static 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))); |
| 9466 | 9479 | ||
| 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); |
| 9469 | 9485 | ||
| 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; |
| 48 | 56 |
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 | } |
| 24 | 26 | ||
| 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 | }; |
| 31 | 33 | ||
| 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 | } |
| 40 | 40 | ||
| 41 | int size() const { | 41 | size_t size() const { |
| 42 | return _size; | 42 | return _entries.length; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 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 | |
| 48 | 48 | // This allows us to take a pointer to an entry in `internal_put` which | |
| 49 | // if we get too full (60%), double the capacity | 49 | // 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 table | 54 | _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 | } |
| 63 | 103 | ||
| ... | @@ -81,40 +121,31 @@ public: | ... | @@ -81,40 +121,31 @@ public: |
| 81 | return internal_get(key); | 121 | return internal_get(key); |
| 82 | } | 122 | } |
| 83 | 123 | ||
| 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 | } |
| 89 | 130 | ||
| 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)) { |
| 96 | 137 | _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 | } |
| 119 | 150 | ||
| 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 array | 164 | // iterator through the entry array |
| 142 | int _index = 0; | 165 | size_t _index = 0; |
| 143 | // used to detect concurrent modification | 166 | // 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 | } |
| 155 | 178 | ||
| 156 | private: | 179 | private: |
| 157 | 180 | // 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; |
| 164 | 195 | ||
| 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 | } |
| 174 | 213 | ||
| 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]; |
| 182 | 234 | 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 rescue | 237 | 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 | } | ||
| 201 | 322 | ||
| 202 | if (!entry->used) { | 323 | Entry *internal_get(const K &key) const { |
| 203 | // adding an entry. otherwise overwriting old value with | 324 | if (_index_bytes == nullptr) { |
| 204 | // same key | 325 | 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 | } | ||
| 207 | 340 | ||
| 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 | } |
| 220 | 357 | ||
| 358 | size_t hash_to_index(uint32_t hash) const { | ||
| 359 | return ((size_t)hash) % _indexes_len; | ||
| 360 | } | ||
| 221 | 361 | ||
| 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; | ||
| 227 | 376 | ||
| 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 | } | ||
| 230 | 384 | ||
| 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 | } |
| 236 | 406 | ||
| 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 | #endif | 420 | #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); |
| 287 | static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right); | 288 | static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right); |
| 288 | static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right); | 289 | static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right); |
| 290 | static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field); | ||
| 289 | 291 | ||
| 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; |
| 824 | 830 | ||
| 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 | } |
| 1172 | 1177 | ||
| 1178 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsyncCallExtra *) { | ||
| 1179 | return IrInstSrcIdAsyncCallExtra; | ||
| 1180 | } | ||
| 1181 | |||
| 1173 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcConst *) { | 1182 | static 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 | } |
| 1628 | 1637 | ||
| 1638 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcSrc *) { | ||
| 1639 | return IrInstSrcIdSrc; | ||
| 1640 | } | ||
| 1629 | 1641 | ||
| 1630 | static constexpr IrInstGenId ir_inst_id(IrInstGenDeclVar *) { | 1642 | static 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 | } |
| 2438 | 2450 | ||
| 2451 | static 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 | |||
| 2439 | static IrInstSrc *ir_build_call_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, | 2470 | static 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 | } |
| 5031 | 5062 | ||
| 5063 | static 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 | } | ||
| 5032 | 5068 | ||
| 5033 | static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { | 5069 | static 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 |
| 6172 | static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *await_node, AstNode *call_node, | 6208 | static 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 | } |
| 6182 | 6217 | ||
| ... | @@ -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; |
| 6197 | 6232 | ||
| 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 | } |
| 6214 | 6266 | ||
| ... | @@ -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 | } |
| 9912 | 9969 | ||
| 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; | ||
| 12591 | 12649 | ||
| 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 pointer | 12651 | // 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 | } |
| 12598 | 12656 | ||
| 12599 | // *[N]T to [*]T | 12657 | // *[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 pointer | 12664 | // 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 | } |
| 12611 | 12669 | ||
| 12612 | // *[N]T to []T | 12670 | // *[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 | } |
| 14704 | 14766 | ||
| 14705 | static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* source_instr, | 14767 | static 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 | } |
| 14711 | 14902 | ||
| 14712 | static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* source_instr, | 14903 | static 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 | } |
| 14850 | 15041 | ||
| 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 | } |
| 14855 | 15046 | ||
| ... | @@ -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 | } |
| 15129 | 15320 | ||
| 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 |
| 15483 | static ZigType *get_ptr_elem_type(CodeGen *g, IrInstGen *ptr) { | 15635 | static 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; |
| 15488 | 15640 | ||
| 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; |
| 19143 | 19295 | ||
| 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 | } |
| 19147 | 19299 | ||
| ... | @@ -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 | } |
| 19289 | 19441 | ||
| 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); |
| 19608 | 19760 | ||
| 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 | } |
| 19860 | 20012 | ||
| 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 | } |
| 20031 | 20183 | ||
| 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 | } |
| 20051 | 20203 | ||
| 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 | } |
| 20161 | 20318 | ||
| 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 | } |
| 20214 | 20371 | ||
| 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; |
| 20366 | 20523 | ||
| 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 | } |
| 20616 | 20771 | ||
| 20617 | static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCallExtra *instruction) { | 20772 | static 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 | |||
| 20817 | static 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; |
| 20622 | 20821 | ||
| 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 | } |
| 20628 | 20827 | ||
| 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 | |||
| 20844 | static 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 | } |
| 20650 | 20857 | ||
| 20858 | static 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 | |||
| 20651 | static IrInstGen *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstSrcCallArgs *instruction) { | 20872 | static 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; |
| 20883 | 21104 | ||
| 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); |
| 20894 | 21106 | ||
| 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; |
| 20896 | 21124 | ||
| 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; |
| 22114 | 22342 | ||
| 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: var | 25121 | // 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: var | 25249 | // 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); |
| 25320 | 25548 | ||
| 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; |
| 25323 | 25551 | ||
| 25324 | // layout: ContainerLayout | 25552 | // 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; |
| 25375 | 25603 | ||
| 25376 | // default_value: var | 25604 | // 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 | } |
| 25401 | 25629 | ||
| 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 | } |
| 25511 | 25754 | ||
| 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_bytes | 25957 | 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 | } |
| 26371 | 26699 | ||
| 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 | } |
| 26375 | 26707 | ||
| ... | @@ -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 | } |
| 26409 | 26741 | ||
| 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 | } |
| 26419 | 26747 | ||
| 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 | } |
| 26422 | 26758 | ||
| ... | @@ -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 | } |
| 28666 | 29032 | ||
| ... | @@ -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 args | 30018 | // 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); |
| 29665 | 30031 | ||
| 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 | } |
| 30771 | 31147 | ||
| 31148 | static 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 | |||
| 30772 | static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruction) { | 31206 | static 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; |
| 31439 | 31879 | ||
| 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/MIT | 5 | * See http://opensource.org/licenses/MIT |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 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 { |
| 55 | static void ir_print_other_inst_src(IrPrintSrc *irp, IrInstSrc *inst); | 56 | static void ir_print_other_inst_src(IrPrintSrc *irp, IrInstSrc *inst); |
| 56 | static void ir_print_other_inst_gen(IrPrintGen *irp, IrInstGen *inst); | 57 | static void ir_print_other_inst_gen(IrPrintGen *irp, IrInstGen *inst); |
| 57 | 58 | ||
| 59 | static 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 | |||
| 58 | const char* ir_inst_src_type_str(IrInstSrcId id) { | 89 | const 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 | } |
| 851 | 886 | ||
| 887 | static 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 | |||
| 852 | static void ir_print_call_args(IrPrintSrc *irp, IrInstSrcCallArgs *instruction) { | 904 | static 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 | } |
| 867 | 919 | ||
| 868 | static void ir_print_call_src(IrPrintSrc *irp, IrInstSrcCall *call_instruction) { | 920 | static 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 | } |
| 912 | 938 | ||
| 913 | static void ir_print_call_gen(IrPrintGen *irp, IrInstGenCall *call_instruction) { | 939 | static 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 | } |
| 1746 | 1746 | ||
| 1747 | static void ir_print_builtin_src(IrPrintSrc *irp, IrInstSrcSrc *instruction) { | ||
| 1748 | fprintf(irp->f, "@src()"); | ||
| 1749 | } | ||
| 1750 | |||
| 1747 | static void ir_print_memset(IrPrintSrc *irp, IrInstSrcMemset *instruction) { | 1751 | static 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 you | 25 | // remember that the pointer to this item is invalid after you |
| 23 | // modify the length of the list | 26 | // 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 | */ |
| 7 | 7 | ||
| 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> | ||
| 11 | 16 | ||
| 12 | #if defined(_WIN32) | 17 | #if defined(_WIN32) |
| 13 | 18 | ||
| ... | @@ -73,6 +78,8 @@ typedef SSIZE_T ssize_t; | ... | @@ -73,6 +78,8 @@ typedef SSIZE_T ssize_t; |
| 73 | #endif | 78 | #endif |
| 74 | 79 | ||
| 75 | #if defined(ZIG_OS_WINDOWS) | 80 | #if defined(ZIG_OS_WINDOWS) |
| 81 | static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le); | ||
| 82 | static size_t utf8_to_utf16le(WCHAR *utf16_le, Slice<uint8_t> utf8); | ||
| 76 | static uint64_t windows_perf_freq; | 83 | static uint64_t windows_perf_freq; |
| 77 | #elif defined(__MACH__) | 84 | #elif defined(__MACH__) |
| 78 | static clock_serv_t macos_calendar_clock; | 85 | static 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); |
| 149 | 156 | ||
| 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); |
| 153 | 160 | ||
| 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); |
| 157 | 170 | ||
| 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 | } |
| 161 | 174 | ||
| 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) { |
| 269 | 282 | ||
| 270 | Error os_path_real(Buf *rel_path, Buf *out_abs_path) { | 283 | Error 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) { |
| 773 | 788 | ||
| 774 | Error os_file_exists(Buf *full_path, bool *result) { | 789 | Error 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 | #else | 794 | #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 | } |
| 1022 | 1038 | ||
| 1023 | Error os_write_file(Buf *full_path, Buf *contents) { | 1039 | Error 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) { |
| 1056 | Error os_dump_file(Buf *src_path, FILE *dest_file) { | 1077 | Error os_dump_file(Buf *src_path, FILE *dest_file) { |
| 1057 | Error err; | 1078 | Error err; |
| 1058 | 1079 | ||
| 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 | } |
| 1174 | 1200 | ||
| 1175 | Error os_copy_file(Buf *src_path, Buf *dest_path) { | 1201 | Error 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 | } |
| 1206 | 1242 | ||
| 1207 | Error os_fetch_file_path(Buf *full_path, Buf *out_contents) { | 1243 | Error 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) { |
| 1230 | 1271 | ||
| 1231 | Error os_get_cwd(Buf *out_cwd) { | 1272 | Error 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 | #else | 1379 | #else |
| ... | @@ -1426,7 +1469,15 @@ Error os_make_path(Buf *path) { | ... | @@ -1426,7 +1469,15 @@ Error os_make_path(Buf *path) { |
| 1426 | 1469 | ||
| 1427 | Error os_make_dir(Buf *path) { | 1470 | Error 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) { |
| 1531 | 1582 | ||
| 1532 | Error os_self_exe_path(Buf *out_path) { | 1583 | Error 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; | ||
| 1546 | 1592 | ||
| 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 | } |
| 1721 | 1767 | ||
| 1768 | // Ported from std.unicode.utf8ByteSequenceLength | ||
| 1769 | static 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.zig | 1777 | // Ported from std/unicode.zig |
| 1723 | static size_t utf8Encode(uint32_t c, Slice<uint8_t> out) { | 1778 | static 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 | } |
| 1755 | 1810 | ||
| 1811 | // Ported from std.unicode.utf8Decode2 | ||
| 1812 | static 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 | ||
| 1826 | static 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 | ||
| 1845 | static 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 | ||
| 1867 | static 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.utf16leToUtf8Alloc | 1885 | // Ported from std.unicode.utf16leToUtf8Alloc |
| 1757 | static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le) { | 1886 | static 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 | ||
| 1904 | static 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 | ||
| 1927 | PathSpace 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 | #endif | 1956 | #endif |
| 1774 | 1957 | ||
| 1775 | // Ported from std.os.getAppDataDir | 1958 | // 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) { |
| 1862 | 2045 | ||
| 1863 | Error os_file_open_rw(Buf *full_path, OsFile *out_file, OsFileAttr *attr, bool need_write, uint32_t mode) { | 2046 | Error 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 CreateFileW | 2048 | 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_ |
| 1967 | 2150 | ||
| 1968 | Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { | 2151 | Error 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); |
| 1973 | 2157 | ||
| 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) |
| 155 | 155 | ||
| 156 | Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths); | 156 | Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths); |
| 157 | 157 | ||
| 158 | const size_t PATH_MAX_WIDE = 32767; | ||
| 159 | |||
| 160 | struct PathSpace { | ||
| 161 | Array<wchar_t, PATH_MAX_WIDE> data; | ||
| 162 | size_t len; | ||
| 163 | }; | ||
| 164 | |||
| 165 | PathSpace slice_to_prefixed_file_w(Slice<uint8_t> path); | ||
| 158 | #endif | 166 | #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 | } |
| 788 | 788 | ||
| 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) |
| 790 | static AstNode *ast_parse_fn_proto(ParseContext *pc) { | 790 | static 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; |
| 822 | 822 | ||
| ... | @@ -870,9 +870,9 @@ static AstNode *ast_parse_container_field(ParseContext *pc) { | ... | @@ -870,9 +870,9 @@ static AstNode *ast_parse_container_field(ParseContext *pc) { |
| 870 | 870 | ||
| 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 | } |
| 2192 | 2192 | ||
| 2193 | // ParamType | 2193 | // ParamType |
| 2194 | // <- KEYWORD_var | 2194 | // <- KEYWORD_anytype |
| 2195 | // / DOT3 | 2195 | // / DOT3 |
| 2196 | // / TypeExpr | 2196 | // / TypeExpr |
| 2197 | static AstNode *ast_parse_param_type(ParseContext *pc) { | 2197 | static 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 | } |
| 2204 | 2204 | ||
| ... | @@ -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) { |
| 2679 | 2679 | ||
| 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 | |||
| 3 | extern "C" { | ||
| 4 | #include "softfloat.h" | ||
| 5 | } | ||
| 6 | |||
| 7 | void 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 | |||
| 17 | void 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 | |||
| 6 | void f128M_abs(const float128_t *aPtr, float128_t *zPtr); | ||
| 7 | void 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 { |
| 159 | 159 | ||
| 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 | } |
| 164 | 164 | ||
| 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 | |||
| 148 | fn 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"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub 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 | }); |
| 90 | 139 | ||
| ... | @@ -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 | }); |
| 118 | 167 | ||
| ... | @@ -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 | }); |
| 1138 | 1187 | ||
| 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 | }); |
| 1804 | 1866 | ||
| ... | @@ -1998,11 +2060,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -1998,11 +2060,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1998 | }); | 2060 | }); |
| 1999 | 2061 | ||
| 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 | }); |
| 2007 | 2069 | ||
| 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 | }); |
| 2803 | 2865 | ||
| 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 | }); |
| 2826 | 2888 | ||
| 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 | }); |
| 2836 | 2898 | ||
| 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 { |
| 3131 | 3193 | ||
| 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 | }); |
| 4356 | 4418 | ||
| 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 | }); |
| 6005 | 6101 | ||
| 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 | }); |
| 6853 | 6949 | ||
| 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 | }); |
| 6862 | 6958 | ||
| 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 | }); |
| 7426 | 7522 | ||
| 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 | ); |
| 759 | 759 | ||
| 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" { |
| 1016 | 1016 | ||
| 1017 | test "@TypeOf an async function call of generic fn with error union type" { | 1017 | test "@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" { |
| 1032 | 1032 | ||
| 1033 | var buf: [100]u8 align(16) = undefined; | 1033 | var buf: [100]u8 align(16) = undefined; |
| 1034 | 1034 | ||
| 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" { |
| 1057 | 1057 | ||
| 1058 | var buf: [100]u8 align(16) = undefined; | 1058 | var buf: [100]u8 align(16) = undefined; |
| 1059 | 1059 | ||
| 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 | } |
| 1072 | 1072 | ||
| 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 | } |
| 1338 | 1338 | ||
| 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 | } |
| 1359 | 1359 | ||
| 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 | } |
| 1382 | 1382 | ||
| 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 | } |
| 1405 | 1405 | ||
| 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" { |
| 171 | 171 | ||
| 172 | test "bitcast passed as tuple element" { | 172 | test "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" { |
| 181 | 181 | ||
| 182 | test "triple level result location with bitcast sandwich passed as tuple element" { | 182 | test "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"); |
| 2 | const expect = std.testing.expect; | 2 | const expect = std.testing.expect; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | 4 | ||
| 5 | fn ctz(x: var) usize { | 5 | fn ctz(x: anytype) usize { |
| 6 | return @ctz(@TypeOf(x), x); | 6 | return @ctz(@TypeOf(x), x); |
| 7 | } | 7 | } |
| 8 | 8 |
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 | } |
| 24 | 24 | ||
| 25 | pub const ArgSerializer = struct { | 25 | pub 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); |
| 28 | 28 | ||
| 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 { |
| 17 | 17 | ||
| 18 | test "Extern function calls in @TypeOf" { | 18 | test "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 | } |
| 23 | 23 | ||
| 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 | } |
| 27 | 27 | ||
| ... | @@ -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 | } |
| 58 | 58 | ||
| 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 | } |
| 62 | 62 | ||
| ... | @@ -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" { |
| 68 | 68 | ||
| 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 @@ | ||
| 1 | const expect = @import("std").testing.expect; | ||
| 2 | |||
| 3 | test "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 | } |
| 15 | 15 | ||
| 16 | fn foo(x: var) void { | 16 | fn foo(x: anytype) void { |
| 17 | bar(x); | 17 | bar(x); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | fn bar(x: var) void { | 20 | fn bar(x: anytype) void { |
| 21 | result = x; | 21 | result = x; |
| 22 | } | 22 | } |
| 23 | 23 |
test/stage1/behavior/call.zig+1-1| ... | @@ -57,7 +57,7 @@ test "tuple parameters" { | ... | @@ -57,7 +57,7 @@ test "tuple parameters" { |
| 57 | 57 | ||
| 58 | test "comptime call with bound function as parameter" { | 58 | test "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 | } |
| 386 | 386 | ||
| 387 | test "@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 | |||
| 387 | test "implicit cast undefined to optional" { | 400 | test "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 | } |
| 210 | 210 | ||
| 211 | fn testEnumTagNameBare(n: var) []const u8 { | 211 | fn testEnumTagNameBare(n: anytype) []const u8 { |
| 212 | return @tagName(n); | 212 | return @tagName(n); |
| 213 | } | 213 | } |
| 214 | 214 | ||
| ... | @@ -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 | |||
| 1144 | test "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 | } |
| 229 | 229 | ||
| 230 | fn handleErrors(err: var) noreturn { | 230 | fn 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 | } |
| 671 | 671 | ||
| 672 | test "variable inside inline loop that has different types on different iterations" { | 672 | test "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 | } |
| 675 | 675 | ||
| 676 | fn testVarInsideInlineLoop(args: var) void { | 676 | fn 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" { |
| 758 | test "*align(1) u16 is the same as *align(1:0:2) u16" { | 758 | test "*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 | } |
| 764 | 764 | ||
| ... | @@ -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{}, |
| 815 | 815 | ||
| 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 | } |
| 820 | 820 | ||
| 821 | pub fn getCpuArch(self: CrossTarget) void { } | 821 | pub fn getCpuArch(self: CrossTarget) void {} |
| 822 | }; | 822 | }; |
| 823 | 823 | ||
| 824 | const DynamicLinker = struct { | 824 | const DynamicLinker = struct { |
| 825 | buffer: [255]u8 = undefined, | 825 | buffer: [255]u8 = undefined, |
| 826 | }; | 826 | }; |
| 827 | |||
| 828 | }; | 827 | }; |
| 829 | 828 | ||
| 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 | } |
| 106 | 106 | ||
| 107 | fn numberLiteralArg(a: var) void { | 107 | fn numberLiteralArg(a: anytype) void { |
| 108 | expect(a == 3); | 108 | expect(a == 3); |
| 109 | } | 109 | } |
| 110 | 110 | ||
| ... | @@ -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 | } |
| 134 | 134 | ||
| 135 | fn addPointCoordsVar(pt: var) i32 { | 135 | fn 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 | } |
| 269 | 269 | ||
| 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 | } |
| 49 | 49 | ||
| 50 | fn max_var(a: var, b: var) @TypeOf(a + b) { | 50 | fn 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 | } |
| 53 | 53 | ||
| ... | @@ -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 | } |
| 135 | 135 | ||
| 136 | const foos = [_]fn (var) bool{ | 136 | const foos = [_]fn (anytype) bool{ |
| 137 | foo1, | 137 | foo1, |
| 138 | foo2, | 138 | foo2, |
| 139 | }; | 139 | }; |
| 140 | 140 | ||
| 141 | fn foo1(arg: var) bool { | 141 | fn foo1(arg: anytype) bool { |
| 142 | return arg; | 142 | return arg; |
| 143 | } | 143 | } |
| 144 | fn foo2(arg: var) bool { | 144 | fn foo2(arg: anytype) bool { |
| 145 | return !arg; | 145 | return !arg; |
| 146 | } | 146 | } |
| 147 | 147 |
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 | } |
| 636 | 636 | ||
| 637 | test "@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 | |||
| 653 | fn testFabs(comptime T: type, x: T) void { | ||
| 654 | const y = -x; | ||
| 655 | const z = @fabs(y); | ||
| 656 | expectEqual(x, z); | ||
| 657 | } | ||
| 658 | |||
| 659 | test "@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 | |||
| 676 | fn testFloor(comptime T: type, x: T) void { | ||
| 677 | const y = x + 0.6; | ||
| 678 | const z = @floor(y); | ||
| 679 | expectEqual(x, z); | ||
| 680 | } | ||
| 681 | |||
| 682 | test "@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 | |||
| 699 | fn testCeil(comptime T: type, x: T) void { | ||
| 700 | const y = x - 0.8; | ||
| 701 | const z = @ceil(y); | ||
| 702 | expectEqual(x, z); | ||
| 703 | } | ||
| 704 | |||
| 705 | test "@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 | |||
| 722 | fn 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 | |||
| 736 | test "@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 | |||
| 753 | fn testRound(comptime T: type, x: T) void { | ||
| 754 | const y = x - 0.5; | ||
| 755 | const z = @round(y); | ||
| 756 | expectEqual(x, z); | ||
| 757 | } | ||
| 758 | |||
| 637 | test "comptime_int param and return" { | 759 | test "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 | |||
| 717 | extern var opaque_extern_var: @Type(.Opaque); | ||
| 718 | var var_to_export: u32 = 42; | ||
| 719 | test "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 lexical | 67 | // test evaluation is always lexical |
| 68 | // ensure that the optional isn't always computed before the non-optional | 68 | // 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 | } |
| 73 | 85 | ||
| 74 | test "passing an optional integer as a parameter" { | 86 | test "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 | }; |
| 284 | 289 | ||
| 285 | S.doTheTest(); | 290 | S.doTheTest(); |
test/stage1/behavior/src.zig created+17| ... | @@ -0,0 +1,17 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | |||
| 4 | test "@src" { | ||
| 5 | doTheTest(); | ||
| 6 | } | ||
| 7 | |||
| 8 | fn 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 | }; |
| 715 | 715 | ||
| 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" { |
| 792 | 792 | ||
| 793 | test "struct with var field" { | 793 | test "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 | |||
| 855 | test "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 | ||
| 2 | typedef 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 @@ | ||
| 1 | const expect = @import("std").testing.expect; | ||
| 2 | |||
| 3 | const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h")); | ||
| 4 | |||
| 5 | test "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(); |
| 43 | 43 | ||
| 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 | } |
| 48 | 48 | ||
| ... | @@ -82,7 +82,7 @@ test "tuple multiplication" { | ... | @@ -82,7 +82,7 @@ test "tuple multiplication" { |
| 82 | 82 | ||
| 83 | test "pass tuple to comptime var parameter" { | 83 | test "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 | } |
| 88 | 88 |
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 | |||
| 217 | test "Type.EnumLiteral" { | ||
| 218 | testTypes(&[_]type{ | ||
| 219 | @TypeOf(.Dummy), | ||
| 220 | }); | ||
| 221 | } | ||
| 222 | |||
| 223 | fn add(a: i32, b: i32) i32 { | ||
| 224 | return a + b; | ||
| 225 | } | ||
| 226 | |||
| 227 | test "Type.Frame" { | ||
| 228 | testTypes(&[_]type{ | ||
| 229 | @Frame(add), | ||
| 230 | }); | ||
| 231 | } | ||
| 232 | |||
| 233 | test "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); |
| 206 | 206 | ||
| 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 | } |
| 252 | 252 | ||
| 253 | const TestStruct = packed struct { | 253 | const 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, |
| 260 | 258 | ||
| 261 | pub fn foo(self: *const Self) void {} | 259 | pub fn foo(self: *const Self) void {} |
| 260 | const Self = @This(); | ||
| 262 | }; | 261 | }; |
| 263 | 262 | ||
| 264 | test "type info: function type info" { | 263 | test "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 | } |
| 283 | 282 | ||
| 284 | extern fn foo(a: usize, b: bool, args: ...) usize; | 283 | extern fn foo(a: usize, b: bool, ...) usize; |
| 285 | 284 | ||
| 286 | test "typeInfo with comptime parameter in struct fn def" { | 285 | test "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 | } |
| 387 | 386 | ||
| 388 | test "defaut value for a var-typed field" { | 387 | test "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 | |||
| 392 | fn add(a: i32, b: i32) i32 { | ||
| 393 | return a + b; | ||
| 394 | } | ||
| 395 | |||
| 396 | test "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 | |||
| 405 | test "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 | |||
| 413 | test "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 | |||
| 429 | test "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 | }; |
| 298 | 298 | ||
| 299 | fn testTaggedUnionInit(x: var) bool { | 299 | fn 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 | |||
| 673 | test "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 @@ |
| 1 | const expect = @import("std").testing.expect; | 1 | const expect = @import("std").testing.expect; |
| 2 | 2 | ||
| 3 | fn add(args: var) i32 { | 3 | fn 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 | } |
| 19 | 19 | ||
| 20 | fn readFirstVarArg(args: var) void { | 20 | fn readFirstVarArg(args: anytype) void { |
| 21 | const value = args[0]; | 21 | const value = args[0]; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| ... | @@ -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 | } |
| 33 | 33 | ||
| 34 | fn addSomeStuff(args: var) i32 { | 34 | fn addSomeStuff(args: anytype) i32 { |
| 35 | return add(args); | 35 | return add(args); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| ... | @@ -47,7 +47,7 @@ test "runtime parameter before var args" { | ... | @@ -47,7 +47,7 @@ test "runtime parameter before var args" { |
| 47 | } | 47 | } |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | fn extraFn(extra: u32, args: var) usize { | 50 | fn 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 | } |
| 59 | 59 | ||
| 60 | const foos = [_]fn (var) bool{ | 60 | const foos = [_]fn (anytype) bool{ |
| 61 | foo1, | 61 | foo1, |
| 62 | foo2, | 62 | foo2, |
| 63 | }; | 63 | }; |
| 64 | 64 | ||
| 65 | fn foo1(args: var) bool { | 65 | fn foo1(args: anytype) bool { |
| 66 | return true; | 66 | return true; |
| 67 | } | 67 | } |
| 68 | fn foo2(args: var) bool { | 68 | fn foo2(args: anytype) bool { |
| 69 | return false; | 69 | return false; |
| 70 | } | 70 | } |
| 71 | 71 | ||
| ... | @@ -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 | } |
| 80 | 80 | ||
| 81 | fn doNothingWithFirstArg(args: var) void { | 81 | fn 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; |
| 393 | 393 | ||
| 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 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const 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. | ||
| 6 | const linux_x64 = std.zig.CrossTarget{ | ||
| 7 | .cpu_arch = .x86_64, | ||
| 8 | .os_tag = .linux, | ||
| 9 | }; | ||
| 10 | |||
| 11 | pub 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 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; | 2 | const 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. | ||
| 5 | const linux_x64 = std.zig.CrossTarget{ | ||
| 6 | .cpu_arch = .x86_64, | ||
| 7 | .os_tag = .linux, | ||
| 8 | }; | ||
| 3 | 9 | ||
| 4 | pub fn addCases(ctx: *TestContext) !void { | 10 | pub 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/1364 | 12 | 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 | } | ||
| 7 | 18 | ||
| 8 | //// hello world | 19 | { |
| 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 | } | ||
| 16 | 121 | ||
| 17 | //// function calling another function | 122 | { |
| 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 @@ |
| 1 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; | 1 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; |
| 2 | const std = @import("std"); | ||
| 3 | |||
| 4 | const ErrorMsg = @import("../../src-self-hosted/Module.zig").ErrorMsg; | ||
| 5 | |||
| 6 | const linux_x64 = std.zig.CrossTarget{ | ||
| 7 | .cpu_arch = .x86_64, | ||
| 8 | .os_tag = .linux, | ||
| 9 | }; | ||
| 2 | 10 | ||
| 3 | pub fn addCases(ctx: *TestContext) !void { | 11 | pub 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/1364 | 13 | \\@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 | ); | ||
| 6 | 64 | ||
| 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 | //); |
| 11 | 84 | ||
| 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 | ||
| 15 | 93 | ||
| 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"); |
| 21 | 99 | ||
| 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"); |
| 27 | 105 | ||
| 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'"); |
| 33 | 111 | ||
| 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"); |
| 39 | 117 | ||
| 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"); |
| 45 | 123 | ||
| 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"); |
| 51 | 129 | ||
| 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; |
| 3 | pub fn addCases(ctx: *TestContext) !void { | 3 | pub 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 | }; |
| 10 | 10 | ||
| 11 | pub fn addCases(ctx: *TestContext) void { | 11 | pub 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 | ); |
| 92 | 87 | ||
| 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 are | 163 | // 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 error | 164 | // 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 | } |
| 219 | 209 | ||
| 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 | ); |
| 435 | 267 | ||
| 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; |
| 4 | const fmt = std.fmt; | 4 | const fmt = std.fmt; |
| 5 | 5 | ||
| 6 | pub fn main() !void { | 6 | pub 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(); |
| 9 | 9 | ||
| 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"); | ||
| 540 | 541 | ||
| 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"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | ||
| 5 | pub fn addCases(cases: *tests.TranslateCContext) void { | 5 | pub 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 > 4 | 56 | \\#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 | }); |
| 1478 | 1503 | ||
| 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*) a | 2709 | \\#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 | }); |
| 2692 | 2717 | ||
| 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 | }); |
| 2802 | 2827 | ||
| 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 | }); |
| 2912 | 2937 | ||
| ... | @@ -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 | }); |
| 2921 | 2946 | ||
| 2922 | if (std.Target.current.abi == .msvc) { | 2947 | if (std.Target.current.abi == .msvc) { |